11.1 方法解析順序
方法解析順序 (Method Resolution Order, MRO) 決定了 Python 在類層次結構中搜索方法和屬性的順序。這在使用多重 繼承時尤其重要,因為類可能從多個父類繼承屬性和方法。
簡單來說,有一個固定的順序(或更確切地說,是算法), 根據這個順序 Python 瀏覽類的繼承樹。這個算法 保證了方法搜索的正確順序,可以描述為:
C3線性化算法
C3線性化算法通過組合以下方式來確定MRO:
- 類本身。
- 按照它們被列舉的順序的父類清單。
- 以相同順序的父類的MRO。
C3線性化算法規則
- 保持方法的局部順序: 如果類
A在類B之前列出,那麼類A的所有方法都應該在 類B的方法之前考慮。 - 保持父類的順序: 如果類
A是類B的父類,那麼類A的所有方法都應該 在類B的方法之前考慮。 - 考慮繼承順序: 如果類
C是 兩個或多個類的父類,那麼類C方法的順序應該在所有這些類的MRO中保持。
算法步驟:
步驟 1. 從類本身開始:
總是從調用方法的類本身開始。
步驟 2. 按照列舉順序添加基類:
在當前類之後,按照繼承時指定的順序檢查其基類。
步驟 3. 瀏覽父類:
那裡查找字段和方法。
步驟 4. 合併父類的MRO:
如果同一個基類通過多個路徑繼承,它 只被檢查一次並且按正確的順序(其餘時間都將被忽略)。
對於已經了解“算法和數據結構”主題的人來說,這 是深度優先搜索,而不是廣度優先搜索。
11.2 檢查MRO
在 Python 中,可以使用屬性 __mro__ 或函數 mro() 檢查類的字段和方法的搜索順序。
範例:
class A:
def method(self):
print("A")
class B(A):
def method(self):
print("B")
class C(A):
def method(self):
print("C")
class D(B, C):
def method(self):
print("D")
# 檢查 MRO
print(D.__mro__)
輸出將是:
(<class '__main__.D'>,
<class '__main__.B'>,
<class '__main__.C'>,
<class '__main__.A'>,
<class 'object'>)
這顯示了 Python 將如何搜索方法和屬性的順序:
D: Python 首先檢查類D中的方法。-
B: 然後 Python 檢查類B中的方法(第一個 父類)。 -
C: 如果在類B中找不到方法,Python 檢查 類C中的方法(第二個父類)。 -
A: 如果在類B和C中找不到方法,Python 檢查類A中的方法。 -
object: 最後,Python 檢查 基礎類object中的方法。
11.3 使用 super() 和 MRO
函數 super() 遵循 MRO 來正確順序呼叫 父類的方法。考慮以下
super() 的使用範例:
class A:
def method(self):
print("A")
super().method()
class B(A):
def method(self):
print("B")
super().method()
class C(A):
def method(self):
print("C")
super().method()
class D(B, C):
def method(self):
print("D")
super().method()
d = D()
d.method()
輸出將如下:
D
B
C
A
搜索順序 (MRO)
1. 調用類 D 的 method 方法:
- Python 首先檢查類
D並在那裡找到方法。 - 方法
D.method()執行並打印"D"。 - 然後調用
super().method(),它遵循 MRO 來調用 下一個方法。
2. 調用類 B 的 method 方法:
- 根據 MRO,
D之後的類是B。 - 方法
B.method()執行並打印"B"。 - 然後調用
super().method(),它遵循 MRO 來調用 下一個方法。
3. 調用類 C 的 method 方法:
- MRO 中
B之後的類是C。 - 方法
C.method()執行並打印"C"。 - 然後調用
super().method(),它遵循 MRO 來調用 下一個方法。
4. 調用類 A 的 method 方法:
- MRO 中
C之後的類是A。 - 方法
A.method()執行並打印"A"。 - 然後調用
super().method(),但因為A沒有其他 父類方法method(除了object),所以調用結束。
GO TO FULL VERSION