大多数面向对象语言都不支持多继承,而在Python中,一个子类是可以同时继承多个父类的,这固然可以带来一个子类可以对多个不同父类加以重用的好处,但也有可能引发著名的 Diamond problem菱形问题(或称钻石问题,有时候也被称为“死亡钻石”),菱形其实就是对下面这种继承结构的形象比喻
这种继承结构下导致的问题称之为菱形问题:如果A中有一个方法,B和/或C都重写了该方法,而D没有重写它,那么D继承的是哪个版本的方法:B的还是C的?如下所示
class A(object): def test(self): print(‘from A‘)??class B(A): def test(self): print(‘from B‘)??class C(A): def test(self): print(‘from C‘)??class D(B,C): pass??obj = D()obj.test() # 结果为:from B
一、继承原理(MRO)
python到底是如何实现继承的呢? 对于你定义的每一个类,Python都会计算出一个方法解析顺序(MRO)列表,该MRO列表就是一个简单的所有基类的线性顺序列表,如下
>>> D.mro() # 新式类内置了mro方法可以查看线性列表的内容,经典类没有该内置该方法[<class ‘__main__.D‘>, <class ‘__main__.B‘>, <class ‘__main__.C‘>, <class ‘__main__.A‘>, <class ‘object‘>]
python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。 而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查2.多个父类会根据它们在列表中的顺序被检查3.如果对下一个类存在两个合法的选择,选择第一个父类
所以obj.test()的查找顺序是,先从对象obj本身的属性里找方法test,没有找到,则参照属性查找的发起者(即obj)所处类D的MRO列表来依次检索,首先在类D中未找到,然后再B中找到方法test
1.由对象发起的属性查找,会从对象自身的属性里检索,没有则会按照对象的类.mro()规定的顺序依次找下去,2.由类发起的属性查找,会按照当前类.mro()规定的顺序依次找下去,
二、深度和广度的优先级
非菱形结构:
参照下述代码,多继承结构为非菱形结构,此时,会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支的顺序直到找到我们想要的属性
class E: def test(self): print(‘from E‘)??class F: def test(self): print(‘from F‘)??class B(E): def test(self): print(‘from B‘)??class C(F): def test(self): print(‘from C‘)??class D: def test(self): print(‘from D‘)??class A(B, C, D): # def test(self): # print(‘from A‘) pass??print(A.mro())‘‘‘[<class ‘__main__.A‘>, <class ‘__main__.B‘>, <class ‘__main__.E‘>, <class ‘__main__.C‘>, <class ‘__main__.F‘>, <class ‘__main__.D‘>, <class ‘object‘>]‘‘‘?obj = A()obj.test() # 结果为:from B# 可依次注释上述类中的方法test来进行验证
菱形结构:
经典类————》python2
如果继承关系为菱形结构,那么经典类与新式类会有不同MRO,分别对应属性的两种查找方式:深度优先和广度优先
class G: # 在python2中,未继承object的类及其子类,都是经典类 def test(self): print(‘from G‘)?class E(G): def test(self): print(‘from E‘)?class F(G): def test(self): print(‘from F‘)?class B(E): def test(self): print(‘from B‘)?class C(F): def test(self): print(‘from C‘)?class D(G): def test(self): print(‘from D‘)?class A(B,C,D): # def test(self): # print(‘from A‘) pass?obj = A()obj.test() # 如上图,查找顺序为:obj->A->B->E->G->C->F->D->object# 可依次注释上述类中的方法test来进行验证,注意请在python2.x中进行测试
新式类————》python3
class G(object): def test(self): print(‘from G‘)?class E(G): def test(self): print(‘from E‘)?class F(G): def test(self): print(‘from F‘)?class B(E): def test(self): print(‘from B‘)?class C(F): def test(self): print(‘from C‘)?class D(G): def test(self): print(‘from D‘)?class A(B,C,D): # def test(self): # print(‘from A‘) pass?obj = A()obj.test() # 如上图,查找顺序为:obj->A->B->E->C->F->D->G->object# 可依次注释上述类中的方法test来进行验证
总结:多继承到底要不用???要用,但是规避几点问题1、继承结构尽量不要过于复杂2、推荐使用mixins机制:在多继承的背景下满足继承的什么"是"什么的关系
原文地址:https://www.cnblogs.com/bailongcaptain/p/12670533.html