Python3多重繼承排序原理(C3算法)


  參考:https://www.jianshu.com/p/c9a0b055947b

     https://xubiubiu.com/2019/06/10/python-%E6%96%B9%E6%B3%95%E8%A7%A3%E6%9E%90%E9%A1%BA%E5%BA%8Fmro-c3%E7%AE%97%E6%B3%95/

  

  類C的線性化記憶為L[C]=[C1,C2,...Cn],其中C1稱為L[C]的頭,其余元素[C2,...Cn]稱為尾。如果一個類C繼承自基類B1,B2,...,B那么L[C]的計算過程為

  

#類object為最高父類,所有類都繼承object
L[objicet]=[object]
L[C(B1,B2,...Bn)]=[C]+merge(L[B1],L[B2],[B1,B2,...Bn])

  merge是將一組列表輸出為一個列表,其過程為

1,檢查第一個列表的頭元素,記做H
2,如果H是后續序列的第一個元素,或者不在后續序列中再次出現,則將其輸出,並將其從所有列表中刪除,如果不符合跳過此元素,查找下一個列表的第一個元素,然后回到步驟1
3,重復上述步驟,直至列表為空或者不能再找出可以輸出的元素。

  舉例說明

>>> class A(object):
...  pass
... 
>>> class B(object):
...  pass
... 
>>> class C(A,B):
...   pass

 

  首先object,A,B的線性化結果比較簡單

L[object]=[object]
L[A]=[A,object]
L[B]=[B,object]

  python內置變量__mro__存儲了

>>> object.__mro__
(<class 'object'>,)
>>> A.__mro__
(<class '__main__.A'>, <class 'object'>)
>>> B.__mro__
(<class '__main__.B'>, <class 'object'>)

  需要計算出L[C]

L[C]=[C]+merge(L[A],L[B],[A,B])
    =[C]+mergr([A,object],[B,object],[A,B])
	#取得的第一個元素是A,是序列[A,B]的第一個元素所以輸出A並且將A從所有列表中刪除
	=[C,A]+merge([object],[B,object],[B])
	#取得的元素為object不滿足條件,object是序列[B,object]的最后一個元素,跳過取到元素為B,滿足條件,將B輸出並從所有列表刪除B
	=[C,A,B]+merge([object],[object])
	#最后的結果
	=[C,A,B,object]

  使用__mro__驗證計算結果正確

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

  一個復雜的例子

class B(object): pass

class C(object): pass

class D(A,C): pass

class E(B,C): pass

class F(D,E): pass

  計算過程

L[F] = [F] + merge(L[D], L[E], [D, E])
     = [F] + merge([D, A, C, object], [E, B, C, object],  [D, E])
     = [F, D] + merge([A, C, object], [E, B, C, object],  [E])
     = [F, D, A] + merge([C, object], [E, B, C, object], [E])
     = [F, D, A, E] + merge([C, object], [B, C, object])
     = [F, D, A, E, B] + merge([C, object], [C, object])
     = [F, D, A, E, B, C, object]

  驗證計算結果

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

  

  以上算法雖然可以計算出繼承順序,但是不直觀 ,可以使用圖示拓撲順序進行推導

  什么是拓撲順序

  在圖論中,拓撲順序(Topological Storting)是一個有向無環圖(DAG,Directed Acyclic Graph)的所有定點的線性序列。且該序列必須滿足一下兩個條件

  1,每個頂點出現且只出現一次

        2,若存在一條從頂點A到頂點B的路徑,那么在序列中頂點A出現在頂點B的前面

  看下圖

 

   它是一個DAG圖,那么如果寫出它的拓撲順序呢?一種比較常見的方法

  1,從DAG途中選擇一個沒有前驅(即入度為0)的頂點並輸出

        2,從圖中刪除該頂點和所有以它為起點的有向邊

        3,重復1和2直到當前DAG圖為空或者當前途中不存在無前驅的頂點為止。

  於是得到拓撲排序后的結果為{1,2,4,3,5}

  看實例

class A(object):
  pass

class B(object):
  pass

class C1(A,B):
  pass

class C2(A,B):
  pass

class D(C1,C2):
  pass

  根據上述繼承關系構成一張圖

  1,找到入度為0的點,只有一個D,把D拿出來,把D相關的邊減掉

   2,現在有兩個入度為0的點(C1,C2),取最左原則,拿C1,減掉C1相關的邊,這時候的排序是{D,C1}

        3, 現在入度為0的點(C2),拿掉C2,減掉C2相關的邊,這時候的排序是{D,C1,C2}

   4,現在入度為0的點(A,B),取最左原則,拿掉A,減掉A相關的邊,這時候的排序是{D,C1,C2,A}

        5,現在入度為0的點只有B,拿掉B,減掉B相關的邊,最后只剩下object

        所以最后的排序是{D,C1,C2,A,B,object}

       驗證一下結果

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

  

  為了進一步屬性,在看一個例子

class A(object):
  pass

class B(object):
  pass

class C1(A):
  pass

class C2(B):
  pass

class D(C1,C2):
  pass

  繼承圖

 

 

  1,找到入度為0的頂點,只有一個D,拿D,剪掉D相關的邊

  2,得到兩個入度為0的頂點(C1,C2),根據最左原則,拿C1,剪掉C1相關的邊,這時候序列為{D,C1}

  3,接着看,入度為0的頂點有兩個(A,C1),根據最左原則,拿A,剪掉A相關的邊,這時候序列為{D,C1,A}

  4,接着看,入度為0的頂點為C2,拿C2,剪掉C2相關的邊,這時候序列為{D,C1,A,C2}

  5,繼續,入度為0的頂點為B,拿B,剪掉B相關的邊,最后還有一個object

  所以最后的序列為{D,C1,A,C2,B,object}

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

  使用圖示拓撲法可以快速計算出繼承順序




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM