在 Python 的物件導向程式設計(OOP)中,
繼承(Inheritance) 是一個核心概念。
當我們只繼承一個父類別時,世界很單純。
但當一個類別同時繼承多個父類別(多重繼承)時,問題就來了:
「如果爸爸有錢,媽媽也有錢,那我到底是繼承爸爸的,還是媽媽的?」
這就是 MRO (Method Resolution Order) 出場的時候。
它是 Python 用來決定 「搜尋方法順序」 的一套交通規則。
本篇教程將涵蓋:
- 什麼是 MRO? 為什麼我們需要它?
- 單一繼承 vs 多重繼承:從簡單的家譜到複雜的繼承網。
- 經典難題:鑽石繼承 (Diamond Problem)。
super()函數的魔術:它是如何沿著 MRO 鏈條傳遞的。
1. 暖身:單一繼承的單純世界
在單一繼承中,MRO 非常直觀:自己 -> 爸爸 -> 阿公 -> … -> Object。
我們可以用 ClassName.__mro__ 來查看這個順序。
class Grandpa:
def say_hi(self):
print("👴 Grandpa: 乖孫你好")
class Dad(Grandpa):
# Dad 沒有覆寫 say_hi
pass
class Son(Dad):
def say_hi(self):
print("👦 Son: 嗨!")
# 建立實例
s = Son()
s.say_hi()
# 查看 MRO
print("\n[Son 的繼承順序 MRO]")
for cls in Son.__mro__:
print(cls)
# output 解讀:
# 1. 雖然 Grandpa 也有 say_hi,但在 Son 自己就找到了,所以印出 "Son: 嗨!"
# 2. MRO 順序最後一定是 <class 'object'>,這是所有 Python 物件的始祖。
2. 進階:多重繼承的兩難
當一個子類別同時繼承兩個父類別,而這兩個父類別都有同名的方法時,Python 該選誰?
規則是:由左至右 (Left-to-Right)。
寫在括號左邊的父類別,優先權較高。class Child(Father, Mother): -> Father 優先class Child(Mother, Father): -> Mother 優先
class Father:
def wealth(self):
print("👨 Father: 我有 100 萬美金")
class Mother:
def wealth(self):
print("👩 Mother: 我有 5000 萬台幣")
# 情境 A: 爸爸優先
class Child_A(Father, Mother):
pass
# 情境 B: 媽媽優先
class Child_B(Mother, Father):
pass
print("=== Child A (Father First) ===")
a = Child_A()
a.wealth()
print(f"MRO: {[c.__name__ for c in Child_A.__mro__]}")
print("\n=== Child B (Mother First) ===")
b = Child_B()
b.wealth()
print(f"MRO: {[c.__name__ for c in Child_B.__mro__]}")
3. 大魔王:鑽石繼承 (The Diamond Problem)
這是多重繼承中最著名的問題。
如果 D 繼承 B 和 C,而 B 和 C 都繼承 A。
這時候形成了一個菱形(鑽石形狀)。
A
/ \
B C
\ /
D
問:如果 D 呼叫了一個方法,順序該是 D -> B -> A -> C 嗎?
❌ 錯! 如果這樣走,A 會被呼叫兩次(一次經由 B,一次經由 C),而且 C 可能會被 A 覆蓋掉(如果 A 在 C 之前被呼叫)。
Python 使用 C3 Linearization 演算法 來解決這個問題,確保:
- 子類別永遠在父類別之前。
- 父類別的順序保持在宣告時的順序 (B 在 C 前)。
- 共同的祖先 (A) 只有在所有子孫 (B, C) 都被搜尋完之後,才會出現。
正確順序是:D -> B -> C -> A。
class A:
def show(self):
print("A: 我是祖先")
class B(A):
def show(self):
print("B: 我是左邊的路")
class C(A):
def show(self):
print("C: 我是右邊的路")
class D(B, C):
pass
d = D()
d.show()
print("\n=== D 的 MRO (鑽石結構) ===")
# 觀察重點:C 雖然是第二順位繼承,但它排在 A 之前!
# 這保證了 C 的邏輯不會被「更古老」的 A 意外覆蓋。
for i, cls in enumerate(D.__mro__):
print(f"{i}. {cls.__name__}")
4. super() 到底是什麼?
很多人誤以為 super() 只是「呼叫父類別」。這在單一繼承是對的,但在多重繼承中,它是 「呼叫 MRO 清單中的下一位」。
這就是為什麼在鑽石結構中,我們可以用 super() 串起所有人的 __init__,而且保證每個人只被執行一次。
下面的例子展示:即使 Left 的父類別是 Base,但因為 MRO 的關係,它的 super() 竟然呼叫到了 Right!這就是 MRO 的魔法。
class Base:
def action(self):
print(" -> Base.action 被執行")
class Left(Base):
def action(self):
print(" -> Left.action 開始")
super().action() # 這裡的 super() 會是誰?在 Diamond 中,它可能不是 Base!
print(" -> Left.action 結束")
class Right(Base):
def action(self):
print(" -> Right.action 開始")
super().action()
print(" -> Right.action 結束")
class Diamond(Left, Right):
def action(self):
print("-> Diamond.action 開始")
super().action()
print("-> Diamond.action 結束")
print("=== MRO 預測 ===")
print("預期順序: Diamond -> Left -> Right -> Base")
print(f"實際 MRO: {[c.__name__ for c in Diamond.__mro__]}")
print("\n=== 執行結果 ===")
d = Diamond()
d.action()
# 驚奇點:
# 注意看 Left 的 super().action() 執行後,跳出來的竟然是 "Right.action"!
# 這是因為在 Diamond 的 MRO 中,Left 的下一棒是 Right。
結論
- MRO 是一張地圖:Python 在這張地圖上尋找方法,找到了就停下來(除非你用
super()叫它繼續找)。 - 左側優先:
class Sub(A, B)中,A 比 B 優先。 - 晚輩優先:子類別永遠比父類別優先。
- 共同祖先墊底:在鑽石繼承中,共同的祖先會排在所有支線都走完之後,避免過早被呼叫。
- 善用
inspect:遇到鬼打牆的繼承問題時,印出.__mro__就能看清真相。
掌握 MRO,你就掌握了 Python 物件導向最複雜也最強大的部分!
推薦hahow線上學習python: https://igrape.net/30afN
5. 番外篇:MRO 與多型 (Polymorphism) 的關係
您可能會好奇:「MRO 跟『多型』有關係嗎?」
答案是:MRO 是 Python 實現繼承式多型的底層機制。
多型 (Polymorphism) 的核心精神是:同一個介面,不同的實作。
當我們呼叫 obj.speak() 時,我們不關心 obj 是貓還是狗,只希望它做出「屬於它自己」的行為。
MRO 保證了這一點:
- 子類別優先:MRO 永遠把子類別排在父類別前面。這確保了當我們呼叫方法時,會執行子類別改寫過的版本(Override),而不是父類別的舊版本。
- 動態綁定:Python 是在執行當下順著 MRO 去找方法的,這讓不同的類別組合可以產生出完全不同的行為流程。
下面的範例展示了 MRO 如何支撐多型的運作:
class Animal:
def speak(self):
print("...")
class Dog(Animal):
def speak(self):
print("🐶 汪汪!")
class Cat(Animal):
def speak(self):
print("🐱 喵喵!")
# 多型的展現:
# 這個函式完全不關心 obj 是誰,它只知道 obj 會 speak
def let_it_speak(obj):
print(f"[{type(obj).__name__}] 試著說話...", end=" ")
obj.speak()
# MRO 在這裡發揮作用:
# 1. 檢查 obj.__class__.__mro__
# 2. 發現 Dog 在 Animal 前面 -> 執行 Dog.speak
# 3. 發現 Cat 在 Animal 前面 -> 執行 Cat.speak
d = Dog()
c = Cat()
let_it_speak(d)
let_it_speak(c)
print("\nMRO 驗證:")
print(f"Dog MRO: {[c.__name__ for c in Dog.mro()]}")
# 如果 MRO 把 Animal 排在 Dog 前面,那不管我們怎麼覆寫,執行的永遠是 "...",多型就失效了。

6. 萬物之源:Object
在所有 MRO 清單的最後一名,您總是會看到 <class 'object'>。這是為什麼?
- 始祖:
object是 Python 中所有類別的最終父類別 (The Ultimate Base Class)。不管您有沒有寫繼承,Python 預設都會讓您繼承它。 - 兜底 (Fallback):它是 MRO 搜尋的終點站。如果連
object身上都找不到您要呼叫的方法,Python 才會死心拋出AttributeError。 - 基本能力:所有物件能
print(__str__)、能比較 (__eq__)、能做雜湊 (__hash__),都是因為繼承了object的能力。
可以把它想像成生物演化的 「單細胞生物」,或者是家族樹裡的 「人類始祖」。它是所有複雜功能的起點。
推薦hahow線上學習python: https://igrape.net/30afN

![Python如何讀取excel檔(.xlsx)?如何用欄標籤提取某一直行?df=pandas.read_excel() ; df[“欄標籤”] Python如何讀取excel檔(.xlsx)?如何用欄標籤提取某一直行?df=pandas.read_excel() ; df[“欄標籤”]](https://i0.wp.com/savingking.com.tw/wp-content/uploads/2022/11/20221109163631_39.png?quality=90&zoom=2&ssl=1&resize=350%2C233)


![Word短篇文件編輯,TQC考題110:重點摘要與評量, \[(*)\] 萬用字元,格式>醒目提示*2次=非醒目提示 Word短篇文件編輯,TQC考題110:重點摘要與評量, \[(*)\] 萬用字元,格式>醒目提示*2次=非醒目提示](https://i2.wp.com/savingking.com.tw/wp-content/uploads/2022/03/20220322172253_75.png?quality=90&zoom=2&ssl=1&resize=350%2C233)





近期留言