一文讲解python中的继承冲突及继承顺序

2024-04-18 0 742
目录
  • 简单的菱形继承
  • 复杂的菱形继承
  • MRO C3 算法
    • 算法原理
    • 算法例子
    • 算法实现
  • 参考资料

    简单的菱形继承

    设计类如下

    一文讲解python中的继承冲突及继承顺序

    设计代码:

    class Animal(object):
    def __init__(self, age: int = None, gender: int = None) -> None:
    print(\”Call the constructor of Animal.\”)
    self.m_age = age
    self.m_gender = gender
    self.m_name = \”Animal\”
    print(\”Ends the call to Animal\’s constructer.\”)
    pass

    def eat(self):
    print(\”Animal is eating\”)

    def sleep(self):
    print(\”Animal is sleeping\”)

    class Tiger(Animal):
    def __init__(self, age: int = None, gender: int = None) -> None:
    print(\”Call the constructor of Tiger.\”)
    self.m_name = \”Tiger\”
    super().__init__(age, gender)
    print(\”Ends the call to Tiger\’s constructer.\”)

    def eat(self):
    print(\”Tiger is eating\”)

    pass

    class Lion(Animal):
    def __init__(self, age: int = None, gender: int = None) -> None:
    print(\”Call the constructor of Lion.\”)
    self.m_name = \”Lion\”
    super().__init__(age, gender)
    print(\”Ends the call to Lion\’s constructer.\”)

    def eat(self):
    print(\”Lion is eating\”)

    def sleep(self):
    print(\”Lion is sleeping\”)
    pass

    class Liger(Tiger, Lion):
    def __init__(self, age: int = None, gender: int = None) -> None:
    super().__init__(age, gender)
    pass

    if __name__ == \’__main__\’:
    liger = Liger(8, 1) #实例化一个`Liger`
    print(Liger.__mro__)
    print(liger.m_name)
    liger.eat()
    liger.sleep()

    运行输出为:

    Call the constructor of Tiger.Call the constructor of Lion.Call the constructor of Animal.Ends the call to Animal's constructer.Ends the call to Lion's constructer.Ends the call to Tiger's constructer.(<class '__main__.Liger'>, <class '__main__.Tiger'>, <class '__main__.Lion'>, <class '__main__.Animal'>, <class 'object'>)AnimalTiger is eatingLion is sleeping

    • 继承顺序:通过构造函数的打印顺序和Liger的mro(显示继承顺序)可以看到,继承顺序为Liger -> Tiger -> Lion -> Animal。多继承时,继承顺序一般为从左到右,从下到上。
    • 同名变量:Animal,Tiger,Lion的初始化函数中都初始化了m_name变量,却并没有同C++中的继承冲突一样出现多份m_name的拷贝,而是只对Liger的实例liger进行动态地修改同一块内存地址,由于Animal的初始化函数最后被调用,所以m_name赋值为Animal。
    • 同名函数:
      • 对于Liger的两父类Tiger,Lion和其祖先Animal都定义过的函数eat,Liger类总是选择最左父类的同名函数,所以调用Liger.eat()得到的是Tiger.eat()。
      • 而如果并不是所有父类都定义过的函数,子类在类型树上从左到右寻找第一个定义过该函数的父类。例如Liger.sleep(),Tiger中并未定义sleep(),所以找到了后面的Lion.sleep()。

    小结:对于简单的菱形继承,可以大致认为其是在类型树上按照"从左到右,从上到下"的广度优先遍历顺序查找成员的。

    复杂的菱形继承

    对于复杂的菱形继承,有时候按照上面的广度优先遍历类型树得到的继承顺序并不正确。例如:

    一文讲解python中的继承冲突及继承顺序

    这时如果使用广度优先遍历得到的继承顺序为:M A B Z X Y object。

    运行如下Python代码得到的继承顺序为:

    class X(object):
    pass

    class Y(object):
    pass

    class Z(object):
    pass

    class A(X, Y):
    pass

    class B(Y, Z):
    pass

    class M(A, B, Z):
    pass

    print(M.mro())

    # [<class '__main__.M'>,<class '__main__.A'>,<class '__main__.X'>,<class '__main__.B'>,<class '__main__.Y'>,<class '__main__.Z'>,<class 'object'>]

    继承顺序为M A X B Y Z object。

    查阅资料得知,这是因为在Python2.3以后的版本,类的继承顺序求法采用了C3算法,以保证继承的单调性原则。(子类不能改变基类的MRO搜索顺序)

    MRO C3 算法

    算法原理

    C3(C3 linearization)算法实现保证了三种重要特性:

    • 继承拓扑图的一致性。
    • 局部优先原则。
    • 单调性原则。

    在C3算法中,把L[C]定义为类C的的linearization值(也就是MRO里的继承顺序,后面简称L值),计算逻辑如下:

    L[C]=C+mergeoflinearizationofparentsofCandlistofparentsofCintheordertheyareinheritedfromlefttoright.

    即L[C]是所有父类的L值的merge。

    运算规则为:

    一文讲解python中的继承冲突及继承顺序

    其中C 多继承父类B 1 . . B N

    merge的运算方法如下:

    • 对于merge的参数列表,从左到右检查每个参数的第一个元素,记为H。
    • 如果H不出现在其它参数中,或者出现在某个参数中且是该参数第一个元素(头),则从所有列表中删去H并添加到C的后面形成C1。(即H不被C的所有父类继承,以保证L值的单调性)

    重复上述步骤直至列表为空或者不能找出可以输出的元素。

    算法例子

    拿上面的继承来举例:

    一文讲解python中的继承冲突及继承顺序

    从上至下一次计算object,X,Y,Z,A,B,M的继承顺序:

    L[object] = O(object)。

    L[X] = X + merge(L[object]) = X + O = XO,同理L[Y] = YO,L[Z] = ZO。

    L[A] = A+merge(L[X],L[Y],XY)
    = A + merge(XO,YO,XY)
    = AX + merge(O,YO,Y)
    = AXY + merge(O,O)
    = AXYO

    L[B] = B + merge(L[Y],L[Z],YZ)
    = B + merge(YO,ZO,YZ)
    = BY + merge(O,ZO,Z)
    = BYZ + merge(O,O)
    = BYZO

    然后是M的继承顺序计算:

    L[M] = M + merge(L[A],L[B],L[Z],ABZ)
    = M + merge(AXYO,BYZO,ZO,ABZ)
    = MA + merge(XYO,BYZO,ZO,BZ)
    = MAX + merge(YO,BYZO,ZO,BZ) #第一个参数中Y被第二个参数中的Z继承,所以检查第二个参数的第一个元素即 B
    = MAXB + merge(YO,YZO,ZO,Z)
    = MAXBY + merge(O,ZO,ZO,Z) #同样,O被Z继承
    = MAXBYZ + merge(O,O,O)
    = MAXBYZO

    得到类M的最终继承顺序MAXBYZO。

    算法实现

    下面是其它资料中找到的Wiki百科上对该算法的Python版本实现:

    def c3MRO(cls):
    if cls is object:
    # 讨论假设顶层基类为object,递归终止
    return [object]

    # 构造C3-MRO算法的总式,递归开始
    mergeList = [c3MRO(baseCls) for baseCls in cls.__bases__]
    mergeList.append(list(cls.__bases__))
    mro = [cls] + merge(mergeList)
    return mro

    def merge(inLists):
    if not inLists:
    # 若合并的内容为空,返回空list
    # 配合下文的排除空list操作,递归终止
    return []

    # 遍历要合并的mro
    for mroList in inLists:
    # 取head
    head = mroList[0]
    # 遍历要合并的mro(与外一层相同),检查尾中是否有head
    ### 此处也遍历了被取head的mro,严格地来说不符合标准算法实现
    ### 但按照多继承中地基础规则(一个类只能被继承一次),
    ### head不可能在自己地尾中,无影响,若标准实现,反而增加开销
    for cmpList in inLists[inLists.index(mroList) + 1:]:
    if head in cmpList[1:]:
    break
    else:
    # 筛选出好head
    nextList = []
    for mergeItem in inLists:
    if head in mergeItem:
    mergeItem.remove(head)
    if mergeItem:
    # 排除空list
    nextList.append(mergeItem)
    # 递归开始
    return [head] + merge(nextList)
    else:
    # 无好head,引发类型错误
    raise TypeError

    测试:

    class A(object):pass
    class B(object):pass
    class C(object):pass
    class E(A,B):pass
    class F(B,C):pass
    class G(E,F):pass

    print([i.__name__ for i in c3MRO(G)])

    输出结果:

    ['G', 'E', 'A', 'F', 'B', 'C', 'object']

    参考资料

    • 带你吃透python的多重继承顺序
    • Python MRO方法解析顺序详解
    • Python多重继承问题-MRO和C3算法

    到此这篇关于一文讲解python中的继承冲突及继承顺序的文章就介绍到这了,更多相关python 继承冲突及继承顺序内容请搜索悠久资源网以前的文章或继续浏览下面的相关文章希望大家以后多多支持悠久资源网!

    您可能感兴趣的文章:

    • Python多继承顺序实例分析

    收藏 (0) 打赏

    感谢您的支持,我会继续努力的!

    打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
    点赞 (0)

    悠久资源 Python 一文讲解python中的继承冲突及继承顺序 https://www.u-9.cn/jiaoben/python/186659.html

    常见问题

    相关文章

    发表评论
    暂无评论
    官方客服团队

    为您解决烦忧 - 24小时在线 专业服务