Python多重继承排序原理(拓扑、C3)

一、什么是拓扑排序

在图论中,拓扑排序(Topological Sorting) 是一个 有向无环图(DAG,Directed Acyclic Graph) 的所有顶点的线性序列。且该序列必须满足下面两个条件:

  • 每个顶点出现且只出现一次。
  • 若存在一条从顶点A到顶点B的路径,那么在序列中顶点A出现在顶点B的前面。

例如,下面这个图:

DAG.jpg

它是一个DAG图,那么如何写出它的拓扑顺序呢?这里说一种比较常用的方法:

  • 从DAG途中选择一个没有前驱(即入度为0)的顶点并输出
  • 从图中删除该顶点和所有以它为起点的有向边。
  • 重复1和2直到当前DAG图为空或当前途中不存在无前驱的顶点为止。后一种情况说明有向图中必然存在环。


    拓扑排序.jpg

于是,得到拓扑排序后的结果是{1,2,4,3,5}

二、什么是C3算法

python多重继承的MRO算法选择: 经典方式、Python2.2 新式算法、Python2.3 新式算法(C3)。Python 3中只保留了最后一种,即C3算法

mro即method resolution order,主要用于在多继承时判断调的属性的路径(来自于哪个类)。mro是基于深度优先搜索算法的。在Python2.3之前是基于此算法,但从Python2.3起应用了新算法:C3算法。

C3算法的本质就是Merge,不断地把mro�()函数返回的序列进行Merge,规则如下:

  • 如果第一个序列的第一个元素,是后续序列的第一个元素,或者不在后续序列中再次出现,则将这个元素合并到最终的方法解析顺序序列中,并从当前操作的全部序列中删除。

  • 如果不符合,则跳过此元素,查找下一个列表的第一个元素,重复1的判断规则

C3算法的解析:
1.多继承UML图:

多继承UML图.jpg

备注:O==object

2.python-C3算法解析:

C3 算法:MRO是一个有序列表L,在类被创建时就计算出来。

L(Child(Base1,Base2))= [ Child + merge( L(Base1),  L(Base2),Base1Base2 )]
L(object)= [ object ]

L的性质:结果为列表,列表中至少有一个元素即类自己。
+: 添加到列表的末尾,即 [ A + B ] = [ A,B ]
merge:

  • ① 如果列表空则结束,非空,读merge中第一个列表的表头,
  • ② 查看该表头是否在merge中所有列表的表尾中。
  • ②-->③ 不在,则放入最终的L中,并从merge中的所有列表中删除,然后回到①中
  • ②-->④ 在,查看当前列表是否是merge中的最后一个列表
  • ④-->⑤ 不是,跳过当前列表,读merge中下一个列表的表头,然后回到 ②中
  • ④-->⑥ 是,异常。类定义失败。

表头: 列表的第一个元素 (列表:ABC,那么表头就是A,B和C就是表尾)
表尾: 列表中表头以外的元素集合(可以为空)
merge 简单的说即寻找合法表头(也就是不在表尾中的表头),如果所有表中都未找到合法表头则异常。

例如:

L(D) = L(D(O))
     = D + merge(L(O))
     = D + O
     = [D,O]
L(B) = L(B(D,E))
     = B + merge(L(D) , L(E))
     = B + merge(DO , EO) # 第一个列表DO的表头D,其他列表比如EO的表尾都不含有D,所以可以将D提出来,即D是合法表头
     = B + D + merge(O , EO) #从第一个开始表头是O,但是后面的列表EO的表尾中含有O所以O是不合法的,所以跳到下一个列表EO
     = B + D + E + merge(O , O)
     = [B,D,E,O]
同理:
L(C) = [C,E,F,O]
L(A(B,C)) = A + merge(L(B),L(C),BC)
          = A + merge(BDEO,CEFO,BC) # B是合法表头
          = A + B + merge(DEO,CEFO,C) # D是合法表头
          = A + B + D + merge(EO,CEFO,C) # E不是合法表头,跳到下一个列表CEFO,此时C是合法表头
          = A + B + D + C + merge(EO,EFO) # 由于第三个列表中的C被删除,为空,所以不存在第三个表,只剩下两个表;此时E是合法表头
          = A + B + D + C + E + merge(O,FO) # O不是合法表头,跳到下一个列表FO,F是合法表头,
          = A + B + D + C + E + F + merge(O,O) # O是合法表头
          = A + B + D + C + E + F + O
          = [A,B,D,C,E,F,O]

获取C3的数组列表,可以梳理清楚子类执行过程中向上执行的顺序

三、Python多重继承

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
class A(object):
    def foo(self):
        print('A foo')
    def bar(self):
        print('A bar')

class B(object):
    def foo(self):
        print('B foo')
    def bar(self):
        print('B bar')

class C1(A,B):
    pass

class C2(A,B):
    def bar(self):
        print('C2-bar')

class D(C1,C2):
    pass

if __name__ == '__main__':
    print(D.__mro__)
    d=D()
    d.foo()
    d.bar()

首先,我们根据上面的继承关系构成一张图,如下


继承关系图.jpg
  • 找到入度为0的点,只有一个D,把D拿出来,把D相关的边剪掉
  • 现在有两个入度为0的点(C1,C2),取最左原则,拿C1,剪掉C1相关的边,这时候的排序是{D,C1}
  • 现在我们看,入度为0的点(C2),拿C2,剪掉C2相关的边,这时候排序是{D,C1,C2}
  • 接着看,入度为0的点(A,B),取最左原则,拿A,剪掉A相关的边,这时候的排序是{D,C1,C2,A}
  • 继续,入度为0的点只有B,拿B,剪掉B相关的边,最后只剩下object
  • 所以最后的排序是{D,C1,C2,A,B,object}

我们执行上面的代码,发现print(D.mro)的结果也正是这样,而这也就是多重继承所使用的C3算法啦

(<class '__main__.D'>, <class '__main__.C1'>, <class '__main__.C2'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
A foo
C2-bar

为了进一步熟悉这个拓扑排序的方法,我们再来一张图,试试看排序结果是怎样的,它继承的内容是否如你所想

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
class A(object):
    def foo(self):
        print('A foo')
    def bar(self):
        print('A bar')

class B(object):
    def foo(self):
        print('B foo')
    def bar(self):
        print('B bar')

class C1(A):
    pass

class C2(B):
    def bar(self):
        print('C2-bar')

class D(C1,C2):
    pass

if __name__ == '__main__':
    print(D.__mro__)
    d=D()
    d.foo()
    d.bar()

还是先根据继承关系构一个继承图


继承关系图2.jpg
  • 找到入度为0的顶点,只有一个D,拿D,剪掉D相关的边
  • 得到两个入度为0的顶点(C1,C2),根据最左原则,拿C1,剪掉C1相关的边,这时候序列为{D,C1}
  • 接着看,入度为0的顶点有两个(A,C1),根据最左原则,拿A,剪掉A相关的边,这时候序列为{D,C1,A}
  • 接着看,入度为0的顶点为C2,拿C2,剪掉C2相关的边,这时候序列为{D,C1,A,C2}
  • 继续,入度为0的顶点为B,拿B,剪掉B相关的边,最后还有一个object
  • 所以最后的序列为{D,C1,A,C2,B,object}

最后,我们执行上面的代码,发现print(D.mro)的结果正如上面所计算的结果

(<class '__main__.D'>, <class '__main__.C1'>, <class '__main__.A'>, <class '__main__.C2'>, <class '__main__.B'>, <class 'object'>)
A foo
A bar

python继承顺序遵循C3算法,只要在一个地方找到了所需的内容,就不再继续查找

参考
python 多重继承之拓扑排序
Python新式类继承的C3算法
python多重继承C3算法

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • 关于MRO 和 C3算法 有一个sina的blog介绍的很好:python多重继承MRO mro即method r...
    英武阅读 5,752评论 2 54
  • https://zh.visualgo.net/graphds 浅谈图形结构https://zh.visualgo...
    狼之独步阅读 3,980评论 0 0
  • 怎样让自己过得充实?我每天都在想,是不是要让自己忙碌起来才是最好的状态?每天都要有所收获,哪怕是很小的,希望每天都...
    Ownery阅读 211评论 0 1
  • 今天闺女上的最喜欢的电子琴社团,回来有点晚,我回家时她还在写作业。她爸跟我讲速算5分钟没有做完,算的有点慢。其它还...
    徐梦赛妈妈李蕾阅读 178评论 0 0
  • 终于,一年结束了。 回过头去看,是一条波浪线,起起伏伏,很好,生活本来的面目,就该是这个样子。 我不想抱怨那些不堪...
    梨子酱欧尼阅读 204评论 2 0