【轉】python---方法解析順序MRO(Method Resolution Order)<以及解決類中super方法>html
對於支持繼承的編程語言來講,其方法(屬性)可能定義在當前類,也可能來自於基類,因此在方法調用時就須要對當前類和基類進行搜索以肯定方法所在的位置。
而搜索的順序就是所謂的「方法解析順序」(Method Resolution Order,或MRO)。
對於只支持單繼承的語言來講,MRO 通常比較簡單;而對於 Python 這種支持多繼承的語言來講,MRO 就複雜不少。
經典類:DFS深度優先搜索(Python2.2之前的版本)
新式類:BFS廣度優先搜索(Python2.2中提出,在與經典類共存的狀況下,是否繼承object是他們的區分方式)
新式類C3算法:Python2.3提出(也是如今Python3惟一支持的方式)
對於下面討論的類的多重繼承:咱們討論兩種狀況。python
在經典類中,沒有__mro__屬性能夠去查看MRO的順序,可是,可使用inspect模塊中getmro方法算法
import inspect
inspect.getmro(類名)
在正常繼承模式下,不會引發任何問題編程
缺點:C類本來是D的子類,如果在C中對D的某個方法進行了重載(B類沒有進行重載),那麼咱們在A中所但願使用的是C中的重載方法,編程語言
可是因爲查找順序是A->B->D->C,因此對於在C類中的重載方法,會在結果D時被過濾(由於在D中查找到該方法就中止了),致使永遠沒法訪問到C中的重載方法ide
import inspect class D: def foo(self): print("D.foo") class C(D): def foo(self): print("C.foo") class B(D): pass class A(B,C): pass print(inspect.getmro(A)) #A->B->D->C obj = A() obj.foo() #D.foo
缺點:B繼承於D,如果D中實現了某個方法,B能夠去調用他,可是C中也自定義了一個同名方法。那麼B會獲取到C中的方法就結束了。這可不是咱們所但願出現的spa
class E(object): pass class D(object): def foo(self): print("D.foo") class C(E): def foo(self): print("C.foo") class B(D): pass class A(B,C): pass print(A.__mro__) #A->B->C->D->E obj = A() obj.foo() #C.foo
在交叉繼承的方式下,不會出現任何問題.net
在python3中這種MRO方法是惟一使用的。3d
推文:你真的理解Python中MRO算法嗎?code
推文:C3算法瞭解
推文:C3算法瞭解(這個更加詳細)
可是上面兩個對於MRO的計算方法都有錯誤處,不過其中第二篇「C3算法瞭解」的評論給出了詳細的解法。下面我也寫出這兩篇文章中的具體解法
注意:咱們把類 C 的線性化(MRO)記爲 L[C] = [C1, C2,…,CN]。其中 C1 稱爲 L[C] 的頭,其他元素 [C2,…,CN] 稱爲尾
L[object] = [object] L[C(B1,B2,...,B(N-1),BN)] = [C] + merge(L[B1]+L[B2]+...+L[B(N-1)]+L[BN], [B1,B2,...,B(N-1),BN]) #這種解法是正確的
其餘地方能夠看上面兩篇推文便可(第二篇更加詳細)
1.檢查第一個列表的頭元素(如 L[B1] 的頭),記做 H。
2.若 H 未出如今其它列表的尾部,則將其輸出,並將其從全部列表中刪除,而後回到步驟1;不然,取出下一個列表的頭部記做 H,繼續該步驟。(重點)
3.重複上述步驟,直至列表爲空或者不能再找出能夠輸出的元素。若是是前一種狀況,則算法結束;若是是後一種狀況,說明沒法構建繼承關係,Python 會拋出異常。
解題步驟:
解題步驟:
可能你發現這種解法,和兩篇推文中的答案一致。可是你向下看,在super方法中提到一個錯誤案例,再去使用這種方法和推文中的方法,就知道該如何使用了。
class A(object): def __init__(self): print("A.__init__.start") print("A.__init__.end") class B(A): def __init__(self): print("B.__init__.start") super(B, self).__init__() print("B.__init__.end") class C(A): def __init__(self): print("B.__init__.start") A.__init__(self) print("B.__init__.end") b = B() # B.__init__.start # A.__init__.start # A.__init__.end # B.__init__.end c = C() # B.__init__.start # A.__init__.start # A.__init__.end # B.__init__.end
>>> class A: ... def __init__(self): ... print("A.__init__.start") ... print("A.__init__.end") ... >>> class B: ... def __init__(self): ... print("B.__init__.start") ... super(B, self).__init__() ... print("B.__init__.end") ... >>> class C(A): ... def __init__(self): ... print("B.__init__.start") ... A.__init__(self) ... print("B.__init__.end") ... >>> b = B() B.__init__.start Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in __init__ TypeError: must be type, not classobj
class E(object): def __init__(self): print("E") class D(object): def __init__(self): print("D") super(D, self).__init__() class C(E): def __init__(self): print("C") super(C, self).__init__() class B(D): def __init__(self): print("B") super(B, self).__init__() class A(B,C): def __init__(self): print("A") super(A, self).__init__() print(A.__mro__) #A->B->D->C->E obj = A() #ABDCE
由最後輸出能夠知道,super是指向MRO順序中本身類的下一個類。
def super(class_name, self):
mro = self.__class__.mro() #獲取mro的列表
return mro[mro.index(class_name) + 1] #獲取本身的索引號,去返回下一個類
class A(object): def __init__(self): print("A.__init__") class B(A): def __init__(self): print("B.__init__") A.__init__(self) class C(B,A): def __init__(self): print("C.__init__") A.__init__(self) B.__init__(self) c = C()
C.__init__
A.__init__
B.__init__
A.__init__ #出現重複調用
注意:
class C(A,B): #會由於沒法建立MRO而出錯 def __init__(self): print("C.__init__") A.__init__(self) B.__init__(self) TypeError: Cannot create a consistent method resolution order (MRO) for bases A, B
class A(object): def __init__(self): print("A.__init__") class B(A): def __init__(self): print("B.__init__") super(B, self).__init__() class C(B,A): def __init__(self): print("C.__init__") super(C, self).__init__() c = C()
C.__init__
B.__init__
A.__init__
一樣:
class C(A,B): #也是由於沒法生成MRO順序出錯 def __init__(self): print("C.__init__") super(C, self).__init__() TypeError: Cannot create a consistent method resolution order (MRO) for bases A, B
正確繼承下的解法:
解法C3算法:
錯誤繼承的緣由:
C3算法解題: