1、改变对象的字符显示
【问题】
你想改变对象实例的打印或显示输出,让它们根据可读性
【解决方案】
要改变一个实例的字符串表示,可以重新定义它的__str__()
和__repr__()
方法。例如:
class Pair:
def __init__(self,x,y):
self.x = x
self.y = y
def __repr__(self):
return 'Pair({0.x!r},{0.y!r})'.format(self)
def __str__(self):
return ({0.x!s},{0.y!s}).format(self)
__repr__()
方法返回一个实例的代码表示形式,通常用来重新构造这个实例。内置repr() 函数返回这个字符串,跟我们使用交互解释器显示的值是一样的。
__str__()
方法将实例转化为一个字符串,使用 str() 或 print() 函数会输出这个字符串。
2、自定义字符串格式化
【问题】
你想通过 format()
函数和字符串方法使得一个对象能够支持自定义的格式化。
【解决方法】
为了自定义字符串的格式化,我们需要在类上面定义__format__()
方法。例如:
_format = {
'ymd':'{d.year}--{d.month}--{d.day}',
'mdy':'{d.month}/{d.day}/{d.year}',
'dmy':'{d.day}/{d.month}/{d.year}',
}
class Date:
def __init__(self,year,month,day):
self.year = year
self.month = month
self.day = day
def __format__(self, code):
if code == '':
code = 'ymd'
fmt = _format[code]
return fmt.format(d=self)
d = Date(2012,12,21)
print(format(d))
#2012--12--21
print(format(d,'mdy'))
#12/21/2012
print('The date is {:ymd}'.format(d))
#The date is 2012--12--21
__format__()
方法给python的字符串格式化功能提供一个钩子,这里需要着重强调的是格式化代码的解析工作完全由类自己决定的。因此,格式化代码可以是任何值。
2、调用父类的方法
【问题】
你想在子类中调用父类的某个已经被覆盖的方法
【解决方案】
为了调用父类(超类)的一个方法,可以使用super()函数,比如:
class A:
def spam(self):
print('A.spam')
class B(A):
def spam(self):
print('B.spam')
super(B, self).spam()
b = B()
b.spam()
#B.spam
#A.spam
super()函数的一个常见的方法是在 __init__()
方法中确保父类被正确的初始化
class A:
def __init__(self):
self.x = 0
class B(A):
def __init__(self):
super(B, self).__init__()
self.y = 1
super()的另外一个常见的用法出现在覆盖 Python 特殊方法的代码中,比如:
class Proxy:
def __init__(self, obj):
self._obj = obj
def __getattr__(self, name):
return getattr(self._obj, name)
def __setattr__(self, name, value):
if name.startswith('_'):
super().__setattr__(name, value)
else:
setattr(self._obj, name, value)
在上面代码中,__setattr__()
的实现包含一个名字的检查。如果某个属性名以下划线(_)开头,就同过super()调用原始的__setattr__()
,否则的话就委派给内部的代理对象self._obj
去处理。这看上去有点意思,因为就算没有显式的指明某个类的父类,super()仍然可以有效的工作。
实际上,大家对于Python中如何正确使用 super()函数普遍知之甚少。有时会看到像下面这样直接调用父类的一个方法:
class Base:
def __init__(self):
print('Base.__init__')
class A(Base):
def __init__(self):
Base.__init__(self)
print('A.__init__')
尽管对于大部分代码而言这么做什么问题,但是在更复杂的涉及到多继承的代码中就有可能导致很奇怪的问题发生。比如,考虑如下情况:
class Base:
def __init__(self):
print('Base.__init__')
class A(Base):
def __init__(self):
Base.__init__(self)
print('A.__init__')
class B(Base):
def __init__(self):
Base.__init__(self)
print('B.__init__')
class C(A,B):
def __init__(self):
A.__init__(self)
B.__init__(self)
print('C.__init__')
# Base.__init__
# A.__init__
# Base.__init__
# B.__init__
# C.__init__
如果你运行这段代码就会发现 Base.__init__()
被调用两次,可能两次调用Base.__init__()
没什么坏处,但有时候却不是。另一方面,假设你在代码中换成是super(),结果就完美了
class Base:
def __init__(self):
print('Base.__init__')
class A(Base):
def __init__(self):
super().__init__()
print('A.__init__')
class B(Base):
def __init__(self):
super(B, self).__init__()
print('B.__init__')
class C(A,B):
def __init__(self):
super().__init__()
print('C.__init__')
# Base.__init__
# B.__init__
# A.__init__
# C.__init__
运行这个新版本后,你会发现每个__init__()
方法只会调用一次了。