在python裡面,如果你使用上qt,sqlalchemy,twisted之類各種大型類庫時候,有時候多重繼承multiple inheritance是個簡單的解決方法,但是多重繼承的複雜性總容易造成誤解和疑惑。
一般「常識」說,使用super訪問父類的屬性/方法,這種說法在多重繼承裡面是不成立的,多重繼承的類並沒有父類的概念(there is no superclass in a mi world)。類似的部落格在過去幾年被人寫了無數遍了,因為過去版本裡面python官方文件對super的解釋非常有限而且有誤導解釋,直到2.6以後的文件,才詳細說明了super在單繼承和多繼承的兩種不同工作方式。當時苦逼的程式設計師甚至不得不去翻看python原始碼才搞清楚是什麼回事。以致幾年來很多人對python的多重繼承保持懷疑態度。
python多重繼承使用method resolution order的動態演算法來解決乙個方法名的呼叫順序,mro其實說來簡單,就是乙個深度優先的繼承列表,很易理解,但隨之來的是遇到互不相同的構造器__init__
引數的問題。
codepad執行結果:
1234567
891011
1213
1415
1617
classa(
object
):def__init__
(self
, arg1):
print"init func in a, with arg1 '%s'" % arg1
super
(a,self
).__init__()
classb(
object
):def__init__
(self
, arg1, arg2):
print"init func in b, with arg1'%s', arg2 '%s'" % (arg1, arg2)
super
(b,self
).__init__
(arg1)
classc(b, a):
def__init__
(self
, arg1, arg2):
print"init func in c, with arg1'%s', arg2 '%s'" % (arg1, arg2)
super
(c,self
).__init__
(arg1, arg2)
printc.__mro__
c = c(
"c's arg1"
,"c's arg2"
)
執行結果:
init func in c, with arg1'c's arg1', arg2 'c's arg2'init func in b, with arg1'c's arg1', arg2 'c's arg2'
init func in a, with arg1 'c's arg1'
(, , , )
可見幾個類的構造器的執行順序正是mro列表的順序。重點是多重繼承的各個類的構造器__init__
之所以能夠執行,是因為每個構造器裡面都有一句super(),這個super完成mro列表中下乙個類的構造器的呼叫。
這個事實聽起來似乎很自然,但看**,b的構造器還得必須知道a的構造器的引數?b需要知道自己將會被c同時繼承a,並且呼叫a的構造?!!很荒謬,但不幸的這是mro的特點。**是可以這麼寫,但不應該,為另乙個不知道什麼時候會被一起繼承的類特地地寫**,跟面對物件設計的解耦原則相違背。
在mro方式的基礎上,這個問題是不可能有效解決的,只能避免。概括起來大概有這麼 兩種方式。
1.使用傳統類的方式,顯式呼叫被繼承類的構造器,避開super的mro自動化工作。
codepad 看執行效果:
1234567
891011
1213
1415
1617
classa(
object
):def__init__
(self
, arg1):
print"init func in a, with arg1 '%s'" % arg1
classb(
object
):def__init__
(self
, arg1, arg2):
print"init func in b, with arg1'%s', arg2 '%s'" % (arg1, arg2)
classc(a, b):
def__init__
(self
, arg1, arg2):
print"init func in c, with arg1'%s', arg2 '%s'" % (arg1, arg2)
#super(c, self).__init__(arg1) #這兩行
a.__init__
(self
, arg1)
#等價 b.__init__
(self
, arg1, arg2)
printc.__mro__
c = c(
"c's arg1"
,"c's arg2"
)
注意 c繼承a,b的順序已經改變。
要排除乙個容易產生的誤解。python文件裡面的super有個很顯著的note:super() only works for new-style classes. super只適用於新類。但新類並不必須使用super。
直接呼叫被繼承類
的__init__
作為unbound方法呼叫,需要指定乙個例項,如self作為引數,依次呼叫各個被繼承類
。缺點是若果這幾個被繼承類
也在構造方法裡面使用這樣呼叫了同乙個上級被繼承類
,會出現「爺爺類」的構造方法被呼叫多次的情況。
如果一定使用super,要注意繼承列表的順序。super(type, self).method呼叫的是mro列表中第乙個,也即繼承列表第乙個類的方法。
pyqt裡面的類內部一般(未細究)都使用__init__
的方式來初始化**,因而很多pyqt的例子**都是使用qtgui.qmainwindow.__init__(self)
這樣的**來初始化。當然在單繼承時候和super的寫法是等價的,但最好使用統一的原則:
乙個簡單好記的原則:
2.使用composition / association pattern的設計模式(即'is-a'轉換成'has-a')來實現相同功能,避免多重繼承。
這個方法聽起來未免有點讓人不快(破壞了原有設計思維),但實際上很可能這是更好的方式,更清晰的**,尤其是要繼承的類裡面混合了使用super
,__init__
兩種初始化方式的時候。
原文:
Python多重繼承的異構構造器
在python裡面,如果你使用上qt,sqlalchemy,twisted之類各種大型類庫時候,有時候多重繼承multiple inheritance是個簡單的解決方法,但是多重繼承的複雜性總容易造成誤解和疑惑。一般 常識 說,使用super訪問父類的屬性 方法,這種說法在多重繼承裡面是不成立的,多...
python的多重繼承
在設計類的繼承關係時,通常,主線都是單一繼承下來的,如果需要 混入 額外的功能,通過多重繼承就可以實現,這種設計通常稱之為mixin。class animal object pass class mammal animal pass class runnable object def run sel...
python的多重繼承
python和c 一樣,支援多繼承。概念雖然容易,但是困難的工作是如果子類呼叫乙個自身沒有定義的屬性,它是按照何種順序去到父類尋找呢,尤其是眾多父類中有多個都包含該同名屬性。對經典類和新式類來說,屬性的查詢順序是不同的。現在我們分別看一下經典類和新式類兩種不同的表現 經典類 usr bin pyth...