前言:
其實這算是為了之後的文章做講解。 這陣子在研究 Design Pattern 使用 Python 的作法 ( 看首頁旁邊的推薦書籍就會發現蹊翹了XD,我看過的書覺得不錯的就會推薦給大家看~) 在實作第一章(Strategy Pattern)的過程中想要做更複雜一點的範例,碰巧發現了 Python 繼承大家常用的 super function 其實並不是繼承的意思,今天就跟大家分享到底 super 是怎麼運作的吧!
這篇會講解到:
- 一般 Python 的繼承方法
- 多重繼承
- mro
- super 的重要性
一般 Python 的繼承方法:
我們用一個蠻常見的例子來舉例 我們創一個交通工具的基礎 Class 吧!
Transportation: 交通工具基礎 Class
1class Transportation:
2 def __init__(self):
3 print("Transportation Init")
4 def __repr__(self):
5 return f""
接下來我們來新增 Transportation 的子類別吧! 我想 Car 也是交通工具的一種,就用它來表示吧!
1class Transportation:
2 def __init__(self):
3 print("Transportation Init")
4 def __repr__(self):
5 return f""
6
7class Car(Transportation):
8 def __init__(self, brend: str):
9 super().__init__() # 這一行決定會不會執行到 繼承的Class 的 Init
10 self.brend = brend
11
12 def __repr__(self):
13 return f""
14
15car = Car("XD") # XD 牌汽車 XDDD
16print(car) #
你可以複製下來跑跑看就會發現跑出來的會是像下面這樣。 ( 因為左右角括號在這裡不能顯示… 所以我就把他先拿掉了)
1Transportation Init
2Car 品牌: XD
不知道大家有沒有注意 code 旁邊的註解 super().init() # 這一行決定會不會執行到
繼承的Class 的 Init
所以這樣看來 super 就是代表 父類的Class 嗎?!很多人會這樣以為,但是其實不是!只是在簡單的例子中剛好就代表著 父類的Class,所以大家才會這樣以為!
為了更好理解,我就用複雜一點的例子來舉例就會發現問題了!
多重繼承:
我先將剛剛的 code 多加上一個 Class 讓它複雜一點
我們來多繼承一個 Vehicle 類別吧!
1class Vehicle:
2 def __init__(self):
3 print("Vehicle Init")
4 def __repr__(self):
5 return f""
1class Transportation:
2 def __init__(self):
3 print("Transportation Init")
4 def __repr__(self):
5 return f""
6
7class Vehicle:
8 def __init__(self):
9 print("Vehicle Init")
10 def __repr__(self):
11 return f""
12
13class Car(Transportation, Vehicle):
14 def __init__(self, brend: str):
15 super().__init__() # 這一行決定會不會執行到 繼承的Class 的 Init
16 self.brend = brend
17
18 def __repr__(self):
19 return f""
20
21car = Car("XD") # XD 牌汽車 XDDD
22print(car) #
執行後會發現只有一行輸出而已,卻沒有 Vehicle Init
的字樣出現!
這是為什麼呢!
這是因為在 Python 當中,繼承的順序在一開始就決定好了,也就是當一個類別繼承多個類別的時候(多重繼承),下一個類別 init 的類別已經定好了,而這個繼承的順序是由一種叫做「C3 線性化算法」的演算法去決定的!
我們不需要知道太底層的演算法,只要記得,在撰寫 class 的時候越靠近左括號的類別會越先執行繼承就好了!
但是我們還是沒有討論到「 為什麼沒執行 print("Vehicle Init")
」這行!
那是因為當我們在 Car.__init__()
中呼叫到 super().__init__()
,而 super().__init__()
代表的是下一個繼承的類別的 __init__()
,但是我們繼承了兩個類別,卻只呼叫到一次 super()
也就代表說我們只會讓一個繼承的類別,也就是 Transportation
讓指定繼承的類別 Init
想要讓指定的繼承類別初始化可以用 類別.__init__()
的方式
1class Car(Transportation, Vehicle):
2 def __init__(self, brend: str):
3 Vehicle.__init__()
4 self.brend = brend
或是可以一口氣讓所有的繼承類別都一起 Init
透過讓每個類別都呼叫 super().init() 來讓每一個 mro 中的類別都呼叫下一個 mro 順位的類別的 init()
1class Transportation:
2 def __init__(self):
3 super().__init__() # 讓下一個 mro 中的類別初始化
4 print("Transportation Init")
5 def __repr__(self):
6 return f""
7
8class Vehicle:
9 def __init__(self):
10 super().__init__() # 讓下一個 mro 中的類別初始化
11 print("Vehicle Init")
12 def __repr__(self):
13 return f""
14
15class Car(Transportation, Vehicle):
16 def __init__(self, brend: str):
17 super().__init__() # 這一行決定會不會執行到 繼承的Class 的 Init
18 self.brend = brend
19
20 def __repr__(self):
21 return f""
22
23car = Car("XD") # XD 牌汽車 XDDD
24print(car) #
結語
mro (method resolution order) 是一個 Python 蠻底層的概念,這對開發者來說也很重要,知道了 mro 的順序就可以讓 多層繼承 或是 多重繼承 的類別照我們想要的順序去初始化,也可以避免菱形繼承的重複初始化問題!
好啦! Python mro 概念介紹到這裡! 下一篇我來跟大家聊聊 不理解 Python mro 的話會寫出怎麼樣的重複初始化的 code 範例!