今天讨论两个话题
* 子类化内置类型的缺点
* 多重继承和方法解析顺序(__mro__)
许多人都对继承敬而远之。Java不支持多继承,并没有产生什么坏的影响,而C++对多继承的滥用上了很多人的心(笔者也是其中一位)。因此,今天就讨论一下多继承到底是怎么回事。
直接子类化内置类型(如继承list、dict、str)容易出错,因为内置类型的方法通常会忽略用户覆盖的方法。因此不要子类化内置类型,用户应该继承collections模块中的类,UserDict、UserList、UserString等,这些类是python提供给用户用来扩展的。
与继承尤其是多继承密切相关的另一个问题是:如果同级的父类有个同名方法或属性,那么python如何决定使用哪一个?
作为一个曾经的C++程序员,经常要面临这个问题。实际上,任何支持多继承的语言都要面临这种潜在的命名冲突,这种冲突由不相关的父类实现了同名的方法引起,这就是经典的”菱形问题“。
举例说明如下:
1 class A: 2 def ping(self): 3 print("ping:", self) 4 5 class B(A): 6 def pong(self): 7 print("pong:", self) 8 9 class C(A): 10 def pong(self): 11 print("PONG:", self) 12 13 class D(B, C): 14 def ping(self): 15 super(D, self).ping() 16 print(‘post-ping:‘, self) 17 18 def pingpong(self): 19 self.ping() 20 print(1) 21 super(D, self).ping() 22 print(2) 23 self.pong() 24 print(3) 25 super(D, self).pong() 26 print(4) 27 C.pong(self)
类B、C继承类A,且都实现了pong方法,但是打印的内容不一样。
如果D的实例调用pong方法的话,调用的是C的还是B的呢?答案是B的pong方法。
类有一个名为__mro__的属性,它是个元组,python会按照__mro__的值按照方法解析出各个父类,知道object类为止。如果想调用父类的方法,推荐使用super()函数。你也可以使用类名.方法(self)的方式调用父类的方法,但是不推荐,如果想绕过方法解析顺序可以使用。
类的继承关系和__mro__解析顺序如下图:
1 d = D() 2 d.ping() 3 print("-------------------------------") 4 d.pingpong() 5 6 7 """ 8 运行结果 9 ping: <__main__.D object at 0x00000000035F62E8> 10 post-ping: <__main__.D object at 0x00000000035F62E8> 11 ------------------------------- 12 ping: <__main__.D object at 0x00000000035F62E8> 13 post-ping: <__main__.D object at 0x00000000035F62E8> 14 1 15 ping: <__main__.D object at 0x00000000035F62E8> 16 2 17 pong: <__main__.D object at 0x00000000035F62E8> 18 3 19 pong: <__main__.D object at 0x00000000035F62E8> 20 4 21 PONG: <__main__.D object at 0x00000000035F62E8> 22 """
方法解析顺序不仅跟继承关系有关,还跟子类中声明的父类顺序有关。如果把类的声明顺序改变,那方法解析顺序也会改变:
1 class E(B, C): 2 pass 3 4 class F(C, B): 5 pass 6 7 print(E.__mro__) 8 print(F.__mro__) 9 10 """ 11 (<class ‘__main__.E‘>, <class ‘__main__.B‘>, <class ‘__main__.C‘>, <class ‘__main__.A‘>, <class ‘object‘>) 12 (<class ‘__main__.F‘>, <class ‘__main__.C‘>, <class ‘__main__.B‘>, <class ‘__main__.A‘>, <class ‘object‘>) 13 """
方法解析顺序依赖于C3算法。详见https://www.python.org/download/releases/2.3/mro/
GUI工具包Tkinter的继承关系图如下:
加入Text的声明顺序是:
class Text(YView, XView, Widget):
...
那么方法解析顺序就应该是:
Text -> YView -> XView -> Widget -> Grid -> Place -> Pack -> BaseWidget -> Misc -> object
《设计模式:可复用面向对象软件的基础》中的适配器模式用的就是多重继承(但是其他22个设计模式都是用单继承,可见多重继承显然是不推荐使用)。
原文:https://www.cnblogs.com/forwardfeilds/p/10485022.html