參考:https://www.jianshu.com/p/c9a0b055947b
類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'>)
使用圖示拓撲法可以快速計算出繼承順序