Python类的多重继承问题深入分析
正文 首先得说明的是,Python的类分为经典类 和 新式类 经典类和新式类的区别在于: 经典类是默认没有派生自某个基类的,而新式类是默认派生自object这个基类的: 复制代码 代码如下: # old style class A():pass # new style 2.经典类在类多重继承的时候是采用从左到右深度优先原则匹配方法的..而新式类是采用C3算法(不同于广度优先)进行匹配的 3.经典类是没有__MRO__和instance.mro()调用的,而新式类是有的. 为什么不用经典类,要更换到新式类 因为在经典类中的多重继承会有些问题...可能导致在继承树中的方法查询绕过后面的父类: 复制代码 代码如下: class A(): def foo1(self): print "A" class B(A): def foo2(self): pass class C(A): def foo1(self): print "C" class D(B,C): pass d = D() 按照经典类的查找顺序从左到右深度优先的规则,在访问d.foo1()的时候,D这个类是没有的..那么往上查找,先找到B,里面没有,深度优先,访问A,找到了foo1(),所以这时候调用的是A的foo1(),从而导致C重写的foo1()被绕过. 所以python引入了新式类的概念,每个基类都继承自object并且,他的匹配规则也从深度优先换到了C3 C3算法 C3算法是怎么做匹配的呢..在问答版块上面讨论之后,归结如下: C3算法的一个核心是merge. 在merge列表中,如果第一个序列mro的第一个类是出现在其它序列,并且也是第一个,或者不出现其它序列,那么这个类就会从这些序列中删除,并合到访问顺序列表中 复制代码 代码如下: class A(O):pass class B(O):pass class C(O):pass class D(A,B):pass class E(C,D):pass 首先需要知道 O(object)的mro(method resolution order)列表是[O,] 复制代码 代码如下: mro(A) = [A,O] mro(B) = [B,O] mro(C) = [C,O] mro(D) = [D] + merge(mro(A),mro(B),[A,B]) = [D] + merge([A,O],[B,B]) = [D,A] + merge([O],[B]) = [D,A,B] + merge([O],[O]) = [D,B,O] mro(E) = [E] + merge(mro(C),mro(D),[C,D]) = [E] + merge([C,[D,D]) = [E,C] + merge([O],[D]) = [E,C,D] + merge([O],O]) = [E,D,[O]) = [E,O] 然后还有一种特殊情况: 新式类生成的访问序列被存储在一个叫MRO的只读列表中.. 最后匹配的时候就按照MRO序列的顺序去匹配了 C3和广度优先的区别: 举个例子就完全明白了: 复制代码 代码如下: class A(object):pass class B(A):pass class C(B):pass class D(A):pass class E(D):pass class F(C,E):pass 按照广度优先遍历,F的MRO序列应该是[F,E,A] 新式类和经典类的super和按类名访问问题 在经典类中,你如果要访问父类的话,是用类名来访问的.. 复制代码 代码如下: class A(): def __init__(self): print "A" class B(A): def __init__(self): print "B" A.__init__(self) #python不会默认调用父类的初始化函数的 这样子看起来没三问题,但是如果类的继承结构比较复杂,会导致代码的可维护性很差.. 复制代码 代码如下: class A(): def __init__(self): print "A" class B(A): def __init__(self): print "B" super(B,self).__init__() 这时候,又有一个问题:当类是多重继承的时候,super访问的是哪一个类呢? super和按照类名访问 混合使用带来的坑 复制代码 代码如下: class A(object): def __init__(self): print "enter A" print "leave A" class B(object): class C(A): class D(A): class F(E,D): 这时候打印出来是: 复制代码 代码如下: enter F enter E enter B leave B enter C enter D enter A leave A leave D leave C leave E enter D enter A leave A leave D leave F 可以看出来D和A的初始化函数被乱入了两次! 所以建议就是要么一直用super,要么一直用按照类名访问 最佳实现: 避免多重继承 以上便是本人对于python类的继承的认识了,希望对大家能有所帮助 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |