python 繼承機制研究
今天遇到了一個 python 程式語言繼承機制的問題,如以下程式碼所示,可以發現子類別 SuperMan
同時繼承了兩個父類別 Woman
、Man
,而且這兩個父類別中都有一個叫 foo
的方法,那 SuperMan
到底會繼承哪一個並呼叫呢 ?
1 | class Woman: |
執行結果如以下,答案是會先呼叫在類別繼承括號中最左邊的的 Woman
類別的方法foo
。若把兩個父類別順序對換,則這次會先從 Man
開始執行。
1 | I am a woman |
而為什麼會這樣執行而不會出錯呢?這個背後的機制就是 python 的繼承順序機制演算法 MRO(Method Resolution Order)定義了繼承的走訪順序,執行以下的程式碼可以看到 MRO 裡面的類別繼承內容。
1 | print(SuperMan.mro()) |
可以發現從最左邊 SuperMan
自己開始,後一個類別就是 Woman
、Man
、object
,這裏要注意的是 python 的類別也是一種物件,因此繼承鍊最後也會指向到object
。
1 | [<class '__main__.SuperMan'>, <class '__main__.Woman'>, <class '__main__.Man'>, <class 'object'>] |
而 MRO 的繼承順序行為背後是有一個 python 的 C3 演算法來實作的,C3 演算法是 python3 的繼承順序機制,這個演算法改進了過去 python2 繼承演算法的問題,結合了 DFS 與 BFS的優缺點,關於 MRO 的詳細過往可以參考這篇文章的介紹。
接著修改了以上的程式如下,在 Woman
、Man
之上又共同繼承了Human
類別,形成了一個棱形繼承的結構,那麼繼承順序會變得如何呢?
1 | class Human: |
程式執行結果如下:
1 | I am a woman |
如果是在棱形繼承的結構之下,MRO 的行為會類似 BFS ,從左邊開始以廣度優先的順序來繼承節點。
然而在不是棱形繼承的結構,而是一般的繼承結構又會如何呢? 這裏更改以上的程式如下,移除 Man
與 Human
的繼承關係。
1 | class Human: |
程式的執行結果會改變如下:
1 | I am a woman |
可以發現繼承的行為改變了!原本繼承 Woman
之後繼承 Man
現在卻變成了繼承 Human
,MRO 在非棱形的繼承結構的行為會與 DFS 相同,變成從左到右以深度優先的方式來繼承節點。
reference:
https://makina-corpus.com/blog/metier/2014/python-tutorial-understanding-python-mro-class-search-path
https://www.python-course.eu/python3_multiple_inheritance.php
https://read01.com/kRkykj.html