Python 多继承

Python 多继承

跟许多高级语言一样,python支持多继承,使用方式也类似

class ClassName(BaseClass1, BaseClass2, BaseClass3):
    '''
    多继承举例
    '''

在python中需要特别注意,多继承在古典类和新式类中MRO(Method Resolution Order, 方法解析顺序)是不同的。

古典类中多继承

在python古典类中,MRO采用对继承树自左至右的深度优先遍历(DFS)。一个经典列子:

'''
     A
   +fun1()
   +fun2()
    /  \
  /      \
 /         \
B           C
+fun1()     +fun1()
            +fun2()
\           /
  \       /
    \   /
      D
'''
class A:
    def fun1(self):
        print "class A, method fun1"
    def fun2(self):
        print "class A, method fun2"

class B(A):
    def fun1(self):
        print "class B, method fun1"

class C(A):
    def fun1(self):
        print "class C, method fun1"
    def fun2(self):
        print "class C, method fun2"

class D(B, C):
    pass

d = D()
d.fun1() #class B, method fun1
d.fun2() #class A, method fun2

用古典类实现,执行D中的fun1, fun2,分别调用B的fun1和A的fun2。根据自左至右深度遍历,继承顺序为D->B->A->C。所以,fun1在B中找到,fun2在A中找到。
DFS算法简单,但是存在一些弊端,在上面的例子中,存在菱形继承关系,C中重写了A的一些方法,将导致D的子类永远无法调用C中重写的方法。

新式类的多继承

新式类的多继承采用C3 MRO搜索。

'''
    object
     |
     A
   +fun1()
   +fun2()
    /  \
  /      \
 /         \
B           C
+fun1()     +fun1()
            +fun2()
\           /
  \       /
    \   /
      D
'''
class A:
    def fun1(self):
        print "class A, method fun1"
    def fun2(self):
        print "class A, method fun2"

class B(A):
    def fun1(self):
        print "class B, method fun1"

class C(A):
    def fun1(self):
        print "class C, method fun1"
    def fun2(self):
        print "class C, method fun2"

class D(B, C):
    pass

d = D()
d.fun1() #class B, method fun1
d.fun2() #class C, method fun2

通过执行结果可以看到新式类与古典类多继承不同。C3 MRO算法的具体描述为:

假定,C1C2,…CN表示类C1到CN的序列,其中序列头部元素(head)= C1,序列尾部(tail)=C2…CN;
C继承的基类自左至右分别为B1, B2, …BN;
mro(C)表示C的线性继承关系,其中mro(object) = object。
算法具体过程:

mro(C(B1, B2, ...BN)) = C + merge( mro(B1), mro(B2)... mro(BN), B1B2...BN)

其中核心算法merge规则:在mro(B1)…mro(BN),B1,…BN中,取mro(B1)的head,如果该元素不在mro(B2), …mro(BN), B1B2…BN的尾部序列中,则添加该元素搞C的线性继承序列中,同时将钙元素从所有的列表中删除(该头元素也叫good head),否则取mro(B2)的head,继续相同的判断,知道整个列表为空或者没有办法找到任何符合要求的头元素(此时引发异常)。

根据算法描述,我们看一下上面的例子。

# O = Object
mro(O) = O
mro(A) = AO
mro(B) = B + merge(mro(A), A) = B + merge(AO, A) = B + A + merge(O) = BAO
mro(C) = C + merge(mro(A), A) = CAO #与mro类似
mro(D) = D + merge(mro(B), mro(C), BC)
       = D + merge(BAO, CAO, BC) # BAO的head B,不在CAO、 BC的tail中,将B加入线性序列
       = D + B + merge(AO, CAO, C) # AO head A,在CAO的tail中,跳过,取CAO head C
       = D + B + C + merge(AO, AO)
       = D + B + C + A + O = DBCAO

继承顺序为D->B->C->A->O,所以,fun1在B中找到,fun2在C中找到。
新式类中可以通过 __mro__ 属性查看,D.__mro__的输出:

(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)

总结

多继承在新式类和古典类的搜索顺序不同,要引起注意。同样需要注意,在C3 MRO中,如果找不到good head会引发异常,因此在多继承设计时,应通过尽量减少菱形继承避免这个问题。

引用

http://www.jianshu.com/p/2ZxRsn
http://www.webtag123.com/python/43896.html
http://python.jobbole.com/85685/
91个建议

你可能感兴趣的:(Python)