流畅的python,Fluent Python 第十二章笔记 (继承)
第12章:继承的优缺点
12.1,子类化内置类型很麻烦
在Python2.2之前,内置类型(如list或dict)不能子类化。在Python2.2.之后,内置类型可以被子类化了,但是有个重要的注意事项:内置类型(使用C语言编写)不会调用类覆盖的特殊方法。
In [194]: class DoppelDict(dict):
...: def __setitem__(self, key, value):
...: super().__setitem__(key, [value] * 2)
...:
In [195]: dd = DoppelDict(one=1)
In [196]: dd
Out[196]: {'one': 1}
In [197]: dd['two'] = 2
In [198]: dd
Out[198]: {'one': 1, 'two': [2, 2]}
In [199]: dd.update(three=3)
In [200]: dd
Out[200]: {'one': 1, 'two': [2, 2], 'three': 3}
In [201]:
通过书中的代码,可以看出定义的__setitem__只有在赋值的时候产生的效果,另外的初始化,升级字典都没有使用修改的__setitem__方法,用了超类的原始方法。
在这个糟糕的局面中__missing__方法却能按预期方式工作,不过这只是特例。
内置方法的类如果调用了其他类的方法,如果被覆盖,也不会被调用。
In [202]: class AnswerDict(dict):
...: def __getitem__(self, key):
...: return 42
...:
In [203]: ad = AnswerDict(a='foo')
In [204]: ad['a']
Out[204]: 42
In [205]: d = {}
In [206]: d.update(ad)
In [207]: d['a']
Out[207]: 'foo'
In [208]: d
Out[208]: {'a': 'foo'}
In [209]:
这个应该更加直观,因为你只在自己的类里面定义了__getitem__,所有当另外对象调用你(这里是升级),并不会使用你修改的方法,还是使用父类list的方法。
直接子类化内置类型(如dict、str、list),因为内置类型的方法通常会忽略用户覆盖的方法。不要子类化内置类型,
应该子类化这三个模块
In [211]: collections.UserDict
Out[211]: collections.UserDict
In [212]: collections.UserList
Out[212]: collections.UserList
In [213]: collections.UserString
Out[213]: collections.UserString
In [219]: import collections
...:
...: class DoppelDict2(collections.UserDict):
...:
...: def __setitem__(self, key, value):
...: return super(DoppelDict2, self).__setitem__(key, [value] *2)
...:
...: def __getitem__(self, item):
...:
...: return self.data[item] * 2
...:
In [220]:
In [220]: dd = DoppelDict2(one=1)
In [221]: dd
Out[221]: {'one': [1, 1]}
In [222]: dd['one']
Out[222]: [1, 1, 1, 1]
In [223]: dd.get('one')
Out[223]: [1, 1, 1, 1]
In [224]: dd.update(three=3)
In [225]: dd
Out[225]: {'one': [1, 1], 'three': [3, 3]}
In [226]:
前面说过,字典的数据在self.data里面,我自己添加的__getitem__属性。
In [226]: class AnswerDict2(UserDict):
...: def __getitem__(self, key):
...: return 42
...:
...:
In [227]: ad = AnswerDict2(a='foo')
In [228]: ad['a']
Out[228]: 42
In [229]: ad
Out[229]: {'a': 'foo'}
In [230]: d = dict()
In [231]: d.update(ad)
In [232]: d
Out[232]: {'a': 42}
In [233]: d['a']
Out[233]: 42
通过这个可以发现,其他对象在调用update的时候,会调用ad的__getitem__方法,由于继承的是UserDict,所有用了修改后的方法。
12.2多重继承和方法解析顺序。
class A(object):
def ping(self):
print('ping', self)
class B(A):
def pong(self):
print('pong', self)
class C(A):
def pong(self):
print('PONG', self)
class D(B, C):
def ping(self):
super(D, self).ping()
print('post-ping', self)
def pingpong(self):
self.ping()
super().ping()
self.pong()
super().pong()
C.pong(self)
In [254]: d = D() In [255]: d.pong() pong <__main__.D object at 0x10e0a4cd0> In [256]: C.pong(d) PONG <__main__.D object at 0x10e0a4cd0> In [257]: D.__mro__ Out[257]: (__main__.D, __main__.B, __main__.C, __main__.A, object) In [258]:
上面执行了两种方式,一种是用实例执行父类的pong,按照__mro__的顺序执行。
还有一种是直接通过类属性执行,显示执行,这个有点强奸的感觉。
一般委托父类执行,用super()比较好。
In [259]: d.ping() ping <__main__.D object at 0x10e0a4cd0> post-ping <__main__.D object at 0x10e0a4cd0>
第一行输出,是根据__mro__一层层寻找,直接找到A的类里面的这个属性执行了。
...: def pingpong(self):
...: self.ping()
...: super().ping()
...: self.pong()
...: super().pong()
...: C.pong(self)
结果
d.pingpong() ping <__main__.D object at 0x10e0a4cd0> post-ping <__main__.D object at 0x10e0a4cd0> ping <__main__.D object at 0x10e0a4cd0> pong <__main__.D object at 0x10e0a4cd0> pong <__main__.D object at 0x10e0a4cd0> PONG <__main__.D object at 0x10e0a4cd0>
最后一个方法的执行。
首相执行self.ping(),自己有这个属性,执行self.ping()后输出
两条执行
第一条ping <__main__.D object at 0x10e0a4cd0>,是执行了父类的父类A的ping方法,
第二条post-ping <__main__.D object at 0x10e0a4cd0>,是自己方法里面执行。
接着super.ping(),就是执行了父类的父类A的ping方法
self.pong与super().pong执行的结果一样,因为self自身没有pong 的属性,需要通过__mro__的顺序找找父类里面这个属性。
super().pong就是通过__mro__去查找父类属性。
最后一个是通过显式的方式运行父类属性。
后面按照书中的代码,演示一些几个类的__mro__
In [269]: bool.__mro__ Out[269]: (bool, int, object) In [270]: import numbers In [271]: numbers.Integral.__mro__ Out[271]: (numbers.Integral, numbers.Rational, numbers.Real, numbers.Complex, numbers.Number, object) In [272]: import io In [273]: io.BytesIO.__mro__ Out[273]: (_io.BytesIO, _io._BufferedIOBase, _io._IOBase, object) In [274]: io.TextIOWrapper.__mro__ Out[274]: (_io.TextIOWrapper, _io._TextIOBase, _io._IOBase, object) In [275]:
书中后面介绍了Tkinter与Django视图中类的继承使用,我的能力有限,两个模块基本没啥实操过,所以书中的很多讲述没办法理解。
只能回头再看。
浙公网安备 33010602011771号