註:此處以python 3為運作環境,範例摘自《python cookbook》第8章。
python中若子類別要實現父類別的初始化,主要有兩種方法,第一種是直接透過父類別名,第二種是利用super方法。在單繼承時兩者沒什麼差別,但在多繼承時就需要注意一些細微的差距了。實例解釋才是硬道理!
1、使用父類別名稱的情況:
Python代碼
class Base:
def __
class A(Base):
def __init__(self):
Base.__init__(self)
def __init__(self):
Base.__init__(self)
print('B.__init__')
class C(A,B): A.__init__(self)
B.__init__(self)
'C.__init__')
此時實例化C類別會輸出如下:Python代碼
>>> c
Base.__init__B.__init__
C.__init__
從中可以看出Base類別被調用了兩次。這想必在很多情況下都不是我們想要的結果,所以此時可考慮用super方法。
2、利用super的情況:
Python代碼 class Base: def ) class A(Base):
def __init__(self):
super().__init__()
print('C.__init__')
此時再實例化C類的輸出為:
P B.__init__ A.__init__ C.__init__ >>>可看出Base類別是不是只調用了一次啊!但很遺憾的是,這並不是促使我寫這篇部落格記錄的原因,因為如果仔細觀察的話,雖說Base類的確如預期只調用了一次,但你有沒有發覺是先輸出“B.__init__”而後才輸出的「A.__init__」?而且為什麼這樣就使得Base只初始化了一次?想必你也有點懵逼了吧?其實這一切都得「怪罪」於super在多繼承時的呼叫過程。 python在實作一個類別(不只是繼承)時,會產生一個方法來產生解析順序列表,該列表可透過類別屬性__mro__ 查看之,如本例中是這樣的:
>Python程式碼 >>> C.__mro__ (>>> Python代碼 class B(Base): 再次實例化C類,輸出如下: Base類別不再產生輸出!為什麼?因為B中沒了super後,就阻斷了清單去搜尋Base類,所以也就沒有初始化Base了!
所以在搜尋一個屬性或方法時,它就會按照這個列表遍歷每個類,直到找到第一個匹配這個屬性或方法的類為止。而在繼承中使用super時,解釋器會每遇到一次super就會在該列表上搜尋下一個類,直到不再遇到super或列表遍歷完為止,然後再類似遞歸逐層返回。因此本例中搜尋過程為:C中遇到super --> 搜尋列表中的下一個類,即A --> A中再次遇到super,搜尋B --> B中super再現,搜尋Base -- > 初始化Base類,遞歸回傳。
為了更好的解釋這個過程,現在請註解掉B類的super所在行:
C. __init__