python中的继承冲突及继承顺序

简单的菱形继承

设计类如下

#mermaid-svg-IYOh6g5InwMB9svy {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-IYOh6g5InwMB9svy .error-icon{fill:#552222;}#mermaid-svg-IYOh6g5InwMB9svy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IYOh6g5InwMB9svy .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-IYOh6g5InwMB9svy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IYOh6g5InwMB9svy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IYOh6g5InwMB9svy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IYOh6g5InwMB9svy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IYOh6g5InwMB9svy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IYOh6g5InwMB9svy .marker.cross{stroke:#333333;}#mermaid-svg-IYOh6g5InwMB9svy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IYOh6g5InwMB9svy .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-IYOh6g5InwMB9svy .cluster-label text{fill:#333;}#mermaid-svg-IYOh6g5InwMB9svy .cluster-label span{color:#333;}#mermaid-svg-IYOh6g5InwMB9svy .label text,#mermaid-svg-IYOh6g5InwMB9svy span{fill:#333;color:#333;}#mermaid-svg-IYOh6g5InwMB9svy .node rect,#mermaid-svg-IYOh6g5InwMB9svy .node circle,#mermaid-svg-IYOh6g5InwMB9svy .node ellipse,#mermaid-svg-IYOh6g5InwMB9svy .node polygon,#mermaid-svg-IYOh6g5InwMB9svy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-IYOh6g5InwMB9svy .node .label{text-align:center;}#mermaid-svg-IYOh6g5InwMB9svy .node.clickable{cursor:pointer;}#mermaid-svg-IYOh6g5InwMB9svy .arrowheadPath{fill:#333333;}#mermaid-svg-IYOh6g5InwMB9svy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-IYOh6g5InwMB9svy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-IYOh6g5InwMB9svy .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-IYOh6g5InwMB9svy .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-IYOh6g5InwMB9svy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-IYOh6g5InwMB9svy .cluster text{fill:#333;}#mermaid-svg-IYOh6g5InwMB9svy .cluster span{color:#333;}#mermaid-svg-IYOh6g5InwMB9svy div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-IYOh6g5InwMB9svy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

object
Animal
Lion
Tiger
Liger

设计代码:

class Animal(object):def __init__(self, age: int = None, gender: int = None) -> None:print("Call the constructor of Animal.")self.m_age = ageself.m_gender = genderself.m_name = "Animal"print("Ends the call to Animal's constructer.")passdef 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")passclass 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")passclass Liger(Tiger, Lion):def __init__(self, age: int = None, gender: int = None) -> None:super().__init__(age, gender)passif __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'>)
Animal
Tiger is eating
Lion is sleeping
  • 继承顺序:通过构造函数的打印顺序和Ligermro(显示继承顺序)可以看到,继承顺序为Liger -> Tiger -> Lion -> Animal。多继承时,继承顺序一般为从左到右,从下到上。
  • 同名变量:AnimalTigerLion的初始化函数中都初始化了m_name变量,却并没有同C++中的继承冲突一样出现多份m_name的拷贝,而是只对Liger的实例liger进行动态地修改同一块内存地址,由于Animal的初始化函数最后被调用,所以m_name赋值为Animal
  • 同名函数:
    • 对于Liger的两父类TigerLion和其祖先Animal都定义过的函数eatLiger类总是选择最左父类的同名函数,所以调用Liger.eat()得到的是Tiger.eat()
    • 而如果并不是所有父类都定义过的函数,子类在类型树上从左到右寻找第一个定义过该函数的父类。例如Liger.sleep()Tiger中并未定义sleep(),所以找到了后面的Lion.sleep()

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

复杂的菱形继承

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

#mermaid-svg-fghoe1s4EtKBJerY {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-fghoe1s4EtKBJerY .error-icon{fill:#552222;}#mermaid-svg-fghoe1s4EtKBJerY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fghoe1s4EtKBJerY .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-fghoe1s4EtKBJerY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fghoe1s4EtKBJerY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fghoe1s4EtKBJerY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fghoe1s4EtKBJerY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fghoe1s4EtKBJerY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fghoe1s4EtKBJerY .marker.cross{stroke:#333333;}#mermaid-svg-fghoe1s4EtKBJerY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fghoe1s4EtKBJerY .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-fghoe1s4EtKBJerY .cluster-label text{fill:#333;}#mermaid-svg-fghoe1s4EtKBJerY .cluster-label span{color:#333;}#mermaid-svg-fghoe1s4EtKBJerY .label text,#mermaid-svg-fghoe1s4EtKBJerY span{fill:#333;color:#333;}#mermaid-svg-fghoe1s4EtKBJerY .node rect,#mermaid-svg-fghoe1s4EtKBJerY .node circle,#mermaid-svg-fghoe1s4EtKBJerY .node ellipse,#mermaid-svg-fghoe1s4EtKBJerY .node polygon,#mermaid-svg-fghoe1s4EtKBJerY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fghoe1s4EtKBJerY .node .label{text-align:center;}#mermaid-svg-fghoe1s4EtKBJerY .node.clickable{cursor:pointer;}#mermaid-svg-fghoe1s4EtKBJerY .arrowheadPath{fill:#333333;}#mermaid-svg-fghoe1s4EtKBJerY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fghoe1s4EtKBJerY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fghoe1s4EtKBJerY .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-fghoe1s4EtKBJerY .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-fghoe1s4EtKBJerY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fghoe1s4EtKBJerY .cluster text{fill:#333;}#mermaid-svg-fghoe1s4EtKBJerY .cluster span{color:#333;}#mermaid-svg-fghoe1s4EtKBJerY div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-fghoe1s4EtKBJerY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

object
X
Y
Z
A
B
M

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

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

class X(object):passclass Y(object):passclass Z(object):passclass A(X, Y):passclass B(Y, Z):passclass M(A, B, Z):passprint(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 + merge of linearization of parents of C and list of parents of C in the order they are inherited from left to right. \text{L[C] = C + merge of linearization of parents of C and list of parents of C in the order they are inherited from left to right.} L[C] = C + merge of linearization of parents of C and list of parents of C in the order they are inherited from left to right.
L[C]是所有父类的L值的merge

运算规则为:
L [ C ( B 1 . . . B n ) ] = C + m e r g e ( L [ B 1 ] , . . . , L [ B N ] , B 1 . . . B N ) L [ o b j e c t ] = o b j e c t \begin{aligned} L[C(B_1...B_n)]&=C+merge(L[B_1],...,L[B_N],B_1...B_N)\\ L[object]&=object \end{aligned} L[C(B1​...Bn​)]L[object]​=C+merge(L[B1​],...,L[BN​],B1​...BN​)=object​
其中 C C C多继承父类 B 1 . . B N B_1..B_N B1​..BN​。

m e r g e merge merge的运算方法如下:

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

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

算法例子

拿上面的继承来举例:

#mermaid-svg-GKrVVrMi3N2ktpmp {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GKrVVrMi3N2ktpmp .error-icon{fill:#552222;}#mermaid-svg-GKrVVrMi3N2ktpmp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-GKrVVrMi3N2ktpmp .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-GKrVVrMi3N2ktpmp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-GKrVVrMi3N2ktpmp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-GKrVVrMi3N2ktpmp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-GKrVVrMi3N2ktpmp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-GKrVVrMi3N2ktpmp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-GKrVVrMi3N2ktpmp .marker.cross{stroke:#333333;}#mermaid-svg-GKrVVrMi3N2ktpmp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-GKrVVrMi3N2ktpmp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-GKrVVrMi3N2ktpmp .cluster-label text{fill:#333;}#mermaid-svg-GKrVVrMi3N2ktpmp .cluster-label span{color:#333;}#mermaid-svg-GKrVVrMi3N2ktpmp .label text,#mermaid-svg-GKrVVrMi3N2ktpmp span{fill:#333;color:#333;}#mermaid-svg-GKrVVrMi3N2ktpmp .node rect,#mermaid-svg-GKrVVrMi3N2ktpmp .node circle,#mermaid-svg-GKrVVrMi3N2ktpmp .node ellipse,#mermaid-svg-GKrVVrMi3N2ktpmp .node polygon,#mermaid-svg-GKrVVrMi3N2ktpmp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-GKrVVrMi3N2ktpmp .node .label{text-align:center;}#mermaid-svg-GKrVVrMi3N2ktpmp .node.clickable{cursor:pointer;}#mermaid-svg-GKrVVrMi3N2ktpmp .arrowheadPath{fill:#333333;}#mermaid-svg-GKrVVrMi3N2ktpmp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-GKrVVrMi3N2ktpmp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-GKrVVrMi3N2ktpmp .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-GKrVVrMi3N2ktpmp .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-GKrVVrMi3N2ktpmp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-GKrVVrMi3N2ktpmp .cluster text{fill:#333;}#mermaid-svg-GKrVVrMi3N2ktpmp .cluster span{color:#333;}#mermaid-svg-GKrVVrMi3N2ktpmp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-GKrVVrMi3N2ktpmp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

object
X
Y
Z
A
B
M

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

L[object] = O(object)

L[X] = X + merge(L[object]) = X + O = XO,同理L[Y] = YOL[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)= AXYOL[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 mrodef merge(inLists):if not inLists:# 若合并的内容为空,返回空list# 配合下文的排除空list操作,递归终止return []# 遍历要合并的mrofor mroList in inLists:# 取headhead = mroList[0]# 遍历要合并的mro(与外一层相同),检查尾中是否有head### 此处也遍历了被取head的mro,严格地来说不符合标准算法实现### 但按照多继承中地基础规则(一个类只能被继承一次),### head不可能在自己地尾中,无影响,若标准实现,反而增加开销for cmpList in inLists[inLists.index(mroList) + 1:]:if head in cmpList[1:]:breakelse:# 筛选出好headnextList = []for mergeItem in inLists:if head in mergeItem:mergeItem.remove(head)if mergeItem:# 排除空listnextList.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):passprint([i.__name__ for i in c3MRO(G)])

输出结果:

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

参考资料

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

python中的继承冲突及继承顺序相关推荐

  1. 理解Python中的继承规则和继承顺序

    先来看一段代码: class First(object):def __init__(self):print ("first")class Second(object):def __ ...

  2. Python中的继承和多态

    本文以生活中的基础现象为切入点,主要介绍了Python基础中继承和多态,包括单继承.多继承的语法.多态常见的 "鸭子类型". 以及如何重写父类的方法都做了详细的讲解. 一.继承的介 ...

  3. python中继承是什么意思_如何理解Python中的继承?python入门

    如何理解Python中的继承?如今,python编程语言深受企业和个人的喜爱.python开发工程师是近年来互联网行业非常热门的职业岗位之一.学习python的人除了零基础的,还有一部分是在职运维.在 ...

  4. 什么是python中子类父类_零基础入门:python中子类继承父类的__init__方法实例

    前言: 今天为大家带来的内容是零基础入门:python中子类继承父类的__init__方法实例!具有不错的参考意义,希望在此能够帮助到各位!(喜欢的话记得点赞转发关注不迷路哦) 使用Python写过面 ...

  5. Python中菱形继承的MRO顺序及property属性

    Python中菱形继承的MRO顺序及property属性 文章目录 Python中菱形继承的MRO顺序及property属性 一.Python中菱形继承的MRO顺序 1. 单独调用父类的方法 2. 多 ...

  6. python中的继承与多态_Python3 与 C# 面向对象之~继承与多态

    2.继承               ¶ 2.1.单继承               ¶ 在OOP中,当我们定义一个Class的时候,可以从某个现有的Class继承 新的Class称为子类,而被继承的 ...

  7. python中的继承有什么特点_python类的继承是什么?类的继承有什么样的规则?

    在这篇文章之中我们来了解一下python类的继承,对于刚刚接触到python这一编程语言的朋友来说,对于python类的继承的了解应该比较少,不过没关系,在接下来的文章之中我们就来了解一下python ...

  8. python中的继承有什么特点_Python类的继承机制是什么

    继承是面向对象的三大特征之一,也是实现代码复用的重要手段.继承经常用于创建和现有类功能类似的新类,又或是新类只需要在现有类基础上添加一些成员(属性和方法),但又不想直接将现有类代码复制给新类. Pyt ...

  9. python中的继承有什么特点_Python中 什么是面向对象-继承和多态

    文字有点长,对于不想看文字的朋友,可以去这里看视频,内容和这个文字一样的,视频可能更好理解 https://www.piqizhu.com/v/zaX9K4pd4GE 上一篇文章<python教 ...

最新文章

  1. 图像处理包括哪些东东?
  2. html5画布显示不出来,运行后html5画布没出来
  3. 基于 Flink+Iceberg 构建企业级实时数据湖 | 附 PPT 下载
  4. 进程的切换和系统的一般执行过程
  5. 区块链100篇之第三篇--数字签名
  6. MySQL 主键相关操作
  7. 13.业务层的事务操作
  8. 亏损208亿,滴滴橙心优选裁员关停!曾融资12亿,最高估值320亿
  9. 记录一个Lock和sychronized应用及双检锁
  10. 美食网页设计作品html,美食网页设计与制作.doc
  11. 【rmzt】动漫性感美女win7主题_8.13
  12. Java实习日记(1)
  13. SM干货篇:你应该具备的提问技巧!
  14. ilo找不到服务器,云计算服务器忘记iLO登录账号的解决方法
  15. 【无标题】阿里滑块 通过 x82y接口、dll、源码 返回x5sec,可解决!
  16. 常用电路标记表示什么意思
  17. 排序算法——冒泡排序(Bubble Sort)
  18. 光模块:PCB设计方法
  19. 如何发布自己的npm包(超详细步骤,博主都在用)
  20. 硬盘突然变raw格式_硬盘突然变为RAW格式解决办法

热门文章

  1. OSChina 初十乱弹 ——我说能,你就信啦?
  2. matlab机器人运动画gif动图
  3. ai2022中文版(支持m1) v26.0.3中文版
  4. FCPX插件:视频降噪插件ProDenoise for Mac破解激活方法
  5. [luogu p1892] [BOI2003]团伙
  6. 2019考研——北京交通大学(计算机技术)复试经验分享
  7. 图像显示的 flickering
  8. VyOS 路由器系统基本配置1
  9. 走近月球,自己做一幅月球地图
  10. 2020年第四届中国 BIM (数字建造)经理高峰论坛即将在杭举办