super使用說明

2021-09-12 03:48:46 字數 4347 閱讀 3121

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,發現都不知怎...