首先得说明的是,Python的类分为经典类 和 新式类
经典类是python2.2以前的东西,可是在2.7还在兼容,可是在3以后的版本就只认可新式类了
新式类在python2.2以后的版本中均可以使用html
object
这个基类的:# old style class A():pass # new style class A(obejct):pass
2.经典类在类多重继承的时候是采用从左到右深度优先
原则匹配方法的..而新式类是采用C3算法
(不一样于广度优先)进行匹配的python
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.foo1()
的时候,D这个类是没有的..那么往上查找,先找到B,里面没有,深度优先,访问A,找到了foo1(),因此这时候调用的是A的foo1(),从而致使C重写的foo1()被绕过.segmentfault
因此python引入了新式类的概念,每一个基类都继承自object
而且,他的匹配规则也从深度优先
换到了C3
函数
C3算法是怎么作匹配的呢..在问答版块上面讨论以后,归结以下:code
C3算法的一个核心是merge
.htm
在merge列表中,若是第一个序列mro的第一个类是出如今其它序列,而且也是第一个,或者不出现其它序列,那么这个类就会从这些序列中删除,并合到访问顺序列表中blog
好比:(引用问题中zhuangzebo的回答@zhuangzebo)继承
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, O], [A, B]) = [D, A] + merge([O], [B, O], [B]) = [D, A, B] + merge([O], [O]) = [D, A, B, O] mro(E) = [E] + merge(mro(C), mro(D), [C, D]) = [E] + merge([C, O], [D, A, B, O], [C, D]) = [E, C] + merge([O], [D, A, B, O], [D]) = [E, C, D] + merge([O], [A, B, O]) = [E, C, D, A, B] + merge([O], [O]) = [E, C, D, A, B, O]
而后还有一种特殊状况:
好比:merge(DO,CO,C)
先merge的是Dmerge(DO,CO,C)
先merge的是C
意思就是.当出现有 一个类出如今两个序列的头(好比C)
这种状况和 这个类只有在一个序列的头(好比D)
这种状况同时出现的时候,按照顺序方式匹配。
新式类生成的访问序列被存储在一个叫MRO的只读列表中..
你可使用instance.__MRO__
或者instance.mro()
来访问
最后匹配的时候就按照MRO序列的顺序去匹配了
举个例子就彻底明白了:
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,C,E,B,D,A]
可是C3是[F,E,D,C,B,A]
意思是你能够当作C3是在一条链路上深度遍历到和另一条链路的交叉点,而后去深度遍历另一条链路,最后遍历交叉点
super
和按类名访问
问题在经典类中,你若是要访问父类的话,是用类名来访问的..
class A(): def __init__(self): print "A" class B(A): def __init__(self): print "B" A.__init__(self) #python不会默认调用父类的初始化函数的
这样子看起来没三问题,可是若是类的继承结构比较复杂,会致使代码的可维护性不好
..
因此新式类推出了super
这个东西...
class A(): def __init__(self): print "A" class B(A): def __init__(self): print "B" super(B,self).__init__()
这时候,又有一个问题:当类是多重继承的时候,super访问的是哪个类呢?
super其实是经过__MRO__
序列来肯定访问哪个类的...实际上就是调用__MRO__
中此类后面的一个类的方法.
好比序列为[F,E,D,C,B,A]
那么F中的super就是E,E的就是D
super
和按照类名访问
混合使用带来的坑class A(object): def __init__(self): print "enter A" print "leave A" class B(object): def __init__(self): print "enter B" print "leave B" class C(A): def __init__(self): print "enter C" super(C, self).__init__() print "leave C" class D(A): def __init__(self): print "enter D" super(D, self).__init__() print "leave D" class E(B, C): def __init__(self): print "enter E" B.__init__(self) C.__init__(self) print "leave E" class F(E, D): def __init__(self): print "enter F" E.__init__(self) D.__init__(self) print "leave F"
这时候打印出来是:
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的初始化函数被乱入了两次!按类名访问
就至关于C语言以前的GOTO
语句...乱跳,而后再用super
按顺序访问..就有问题了
因此建议就是要么一直用super
,要么一直用按照类名访问