python 构造函数继承_Python多重继承的异构构造器
在Python里面,如果你使用上Qt,SQLAlchemy,Twisted之類各種大型類庫時候,有時候多重繼承Multiple Inheritance是個簡單的解決方法,但是多重繼承的復雜性總容易造成誤解和疑惑。
一般“常識”說,使用super訪問父類的屬性/方法,這種說法在多重繼承里面是不成立的,多重繼承的類并沒有父類的概念(There is no superclass in a MI world)。類似的博客在過去幾年被人寫了無數(shù)遍了,因為過去版本里面python官方文檔對super的解釋非常有限而且有誤導解釋,直到2.6以后的文檔,才詳細說明了super在單繼承和多繼承的兩種不同工作方式。當時苦逼的程序員甚至不得不去翻看Python源碼才搞清楚是什么回事。以致幾年來很多人對python的多重繼承保持懷疑態(tài)度。
Python多重繼承使用Method Resolution Order的動態(tài)算法來解決一個方法名的調用順序,mro其實說來簡單,就是一個深度優(yōu)先的繼承列表,很易理解,但隨之來的是遇到互不相同的構造器__init__參數(shù)的問題。
Codepad運行結果:
class A(object):
def __init__(self, arg1):
print "init func in A, with arg1 '%s'" % arg1
super(A, self).__init__()
class B(object):
def __init__(self, arg1, arg2):
print "init func in B, with arg1'%s', arg2 '%s'" % (arg1, arg2)
super(B, self).__init__(arg1)
class C(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)
print C.__mro__
c = C("C's arg1", "C's arg2")
執(zhí)行結果:
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'
(, , , )
可見幾個類的構造器的執(zhí)行順序正是mro列表的順序。重點是多重繼承的各個類的構造器__init__之所以能夠執(zhí)行,是因為每個構造器里面都有一句super(),這個super完成mro列表中下一個類的構造器的調用。
這個事實聽起來似乎很自然,但看代碼,B的構造器還得必須知道A的構造器的參數(shù)?B需要知道自己將會被C同時繼承A,并且調用A的構造?!!很荒謬,但不幸的這是mro的特點。代碼是可以這么寫,但不應該,為另一個不知道什么時候會被一起繼承的類特地地寫代碼,跟面對對象設計的解耦原則相違背。How
在mro方式的基礎上,這個問題是不可能有效解決的,只能避免。概括起來大概有這么 兩種方式。
1.使用傳統(tǒng)類的方式,顯式調用被繼承類的構造器,避開super的mro自動化工作。
Codepad 看運行效果:
class A(object):
def __init__(self, arg1):
print "init func in A, with arg1 '%s'" % arg1
class B(object):
def __init__(self, arg1, arg2):
print "init func in B, with arg1'%s', arg2 '%s'" % (arg1, arg2)
class C(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)
print C.__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作為參數(shù),依次調用各個被繼承類。缺點是若果這幾個被繼承類也在構造方法里面使用這樣調用了同一個上級被繼承類,會出現(xiàn)“爺爺類”的構造方法被調用多次的情況。
如果一定使用super,要注意繼承列表的順序。super(TYPE, self).method調用的是mro列表中第一個,也即繼承列表第一個類的方法。
PyQt里面的類內部一般(未細究)都使用__init__的方式來初始化代碼,因而很多PyQt的例子代碼都是使用QtGui.QMainWindow.__init__(self)這樣的代碼來初始化。當然在單繼承時候和super的寫法是等價的,但最好使用統(tǒng)一的原則:
一個簡單好記的原則:
如果”被繼承類”都使用__init__,”繼承類”就使用__init__來初始化;
如果”被繼承類”都使用super,”繼承類”就使用super來初始化;
2.使用Composition / Association Pattern的設計模式(即’Is-A’轉換成’Has-A’)來實現(xiàn)相同功能,避免多重繼承。
這個方法聽起來未免有點讓人不快(破壞了原有設計思維),但實際上很可能這是更好的方式,更清晰的代碼,尤其是要繼承的類里面混合了使用super,__init__兩種初始化方式的時候。;
玩蛇網(wǎng)文章,轉載請注明出處和文章網(wǎng)址:https://www.iplaypy.com/jinjie/jj5808.html
相關文章 Recommend
總結
以上是生活随笔為你收集整理的python 构造函数继承_Python多重继承的异构构造器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 步苹果iOS的后尘,谷歌Android1
- 下一篇: python缺失值类型与分析_3.1.1