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

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

     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]的計算過程爲blog

  

#類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的線性化結果比較簡單get

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

  python內置變量__mro__存儲了class

>>> 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__驗證計算結果正確cli

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

  一個複雜的例子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'>)

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

相關文章
相關標籤/搜索