super可以呼叫父類有兩個原因,第一 ,要初始化父類,因為在子類中也存在初始化方法,所以無法確認初始化是子類還是父類的,此時需要制定super()方法,特☞父類的初始化方法,
第二 子類和父類有相同名稱的方法,此時正常情況下,呼叫父類的方法會被子類覆蓋,所以需要指定使用父類的方法,此時需要使用super()
8.7 呼叫父類方法
問題你想在子類中呼叫父類的某個已經被覆蓋的方法。
解決方案
為了呼叫父類(超類)的乙個方法,可以使用 super() 函式,比如:
class a:
def spam(self):
print('a.spam')
class b(a):
def spam(self):
print('b.spam')
super().spam() # call parent spam()
super() 函式的乙個常見用法是在 __init__() 方法中確保父類被正確的初始化了:
class a:
def __init__(self):
self.x = 0
class b(a):
def __init__(self):
super().__init__()
self.y = 1
super() 的另外乙個常見用法出現在覆蓋python特殊方法的**中,比如:
class proxy:
def __init__(self, obj):
self._obj = obj
# delegate attribute lookup to internal obj
def __getattr__(self, name):
return getattr(self._obj, name)
# delegate attribute assignment
def __setattr__(self, name, value):
if name.startswith('_'):
super().__setattr__(name, value) # call original __setattr__
else:
setattr(self._obj, name, value)
在上面**中,__setattr__() 的實現包含乙個名字檢查。 如果某個屬性名以下劃線(_)開頭,就通過 super() 呼叫原始的 __setattr__() , 否則的話就委派給內部的**物件 self._obj 去處理。 這看上去有點意思,因為就算沒有顯式的指明某個類的父類, super() 仍然可以有效的工作。
討論class base:
def __init__(self):
print('base.__init__')
class a(base):
def __init__(self):
base.__init__(self)
print('a.__init__')
儘管對於大部分**而言這麼做沒什麼問題,但是在更複雜的涉及到多繼承的**中就有可能導致很奇怪的問題發生。 比如,考慮如下的情況:
class base:
def __init__(self):
print('base.__init__')
class a(base):
def __init__(self):
base.__init__(self)
print('a.__init__')
class b(base):
def __init__(self):
base.__init__(self)
print('b.__init__')
class c(a,b):
def __init__(self):
a.__init__(self)
b.__init__(self)
print('c.__init__')
如果你執行這段**就會發現 base.__init__() 被呼叫兩次,如下所示:
>>> c = c()
base.__init__
a.__init__
base.__init__
b.__init__
c.__init__
>>>
可能兩次呼叫 base.__init__() 沒什麼壞處,但有時候卻不是。 另一方面,假設你在**中換成使用 super() ,結果就很完美了:
class base:
def __init__(self):
print('base.__init__')
class a(base):
def __init__(self):
super().__init__()
print('a.__init__')
class b(base):
def __init__(self):
super().__init__()
print('b.__init__')
class c(a,b):
def __init__(self):
super().__init__() # only one call to super() here
print('c.__init__')
執行這個新版本後,你會發現每個 __init__() 方法只會被呼叫一次了:
>>> c = c()
base.__init__
b.__init__
a.__init__
c.__init__
>>>
為了弄清它的原理,我們需要花點時間解釋下python是如何實現繼承的。 對於你定義的每乙個類,python會計算出乙個所謂的方法解析順序(mro)列表。 這個mro列表就是乙個簡單的所有基類的線性順序表。例如:
>>> c.__mro__
(, , ,
, )>>>
為了實現繼承,python會在mro列表上從左到右開始查詢基類,直到找到第乙個匹配這個屬性的類為止。
而這個mro列表的構造是通過乙個c3線性化演算法來實現的。 我們不去深究這個演算法的數學原理,它實際上就是合併所有父類的mro列表並遵循如下三條準則:
子類會先於父類被檢查
多個父類會根據它們在列表中的順序被檢查
如果對下乙個類存在兩個合法的選擇,選擇第乙個父類
老實說,你所要知道的就是mro列表中的類順序會讓你定義的任意類層級關係變得有意義。
class a:
def spam(self):
print('a.spam')
super().spam()
如果你試著直接使用這個類就會出錯:
>>> a = a()
>>> a.spam()
a.spam
traceback (most recent call last):
file "", line 1, in file "", line 4, in spam
attributeerror: 'super' object has no attribute 'spam'
>>>
但是,如果你使用多繼承的話看看會發生什麼:
>>> class b:
... def spam(self):
... print('b.spam')
...>>> class c(a,b):
... pass
...>>> c = c()
>>> c.spam()
a.spam
b.spam
>>>
你可以看到在類a中使用 super().spam() 實際上呼叫的是跟類a毫無關係的類b中的 spam() 方法。 這個用類c的mro列表就可以完全解釋清楚了:
>>> c.__mro__
(, , ,
)>>>
在定義混入類的時候這樣使用 super() 是很普遍的。可以參考8.13和8.18小節。
然而,由於 super() 可能會呼叫不是你想要的方法,你應該遵循一些通用原則。 首先,確保在繼承體系中所有相同名字的方法擁有可相容的引數簽名(比如相同的引數個數和引數名稱)。 這樣可以確保 super() 呼叫乙個非直接父類方法時不會出錯。 其次,最好確保最頂層的類提供了這個方法的實現,這樣的話在mro上面的查詢鏈肯定可以找到某個確定的方法。
在python社群中對於 super() 的使用有時候會引來一些爭議。 儘管如此,如果一切順利的話,你應該在你最新**中使用它。 raymond hettinger為此寫了一篇非常好的文章 「python』s super() considered super!」 , 通過大量的例子向我們解釋了為什麼 super() 是極好的。
使用說明 附註工具使用說明
附註工具使用說明 附註工具用途 附註工具主要用於更新利用word附註應用程式生成的帶域 的附註,該工具在word右鍵 更新鏈結 的基礎上進行了優化,故在使用時,不能再利用word右鍵 更新鏈結 而要用本工具的 更新當前鏈結 或 更新所有鏈結 使用說明 一 更換路徑 當利用word附註應用程式生成帶域...
使用說明 農用遮光網使用說明
農用遮光網使用說明 建築防塵網購買方 建築施工企業 在購買建築防塵網時,應該對 作出比較,可以分辨品牌 型號,且購買時應該在一定程度上了解信譽良莠。建築防塵網購買方 市場售賣方 在選購建築防塵網時,可以把 作為基礎,好的 可以用來彌補信譽不足,而差的 則需要按照你的服務收費。農用遮光網使用說明 用途...
Hibernate tools 使用說明
05年的8月份第一次接觸hibernate tools,使用起來感覺還不錯,但也沒有深入研究,後來由於一直在做乙個專案,再後來用了一陣myeclipse,現在換了公司,使用wtp all in one eclipse的乙個版本 生成domain的時候使用了hibernate tools,發現都不知怎...