目錄
- 一、super( ) 的用途
- 二、了解 super 的基礎信息
- 三、典型用法
- 3.1 單繼承問題
- 3.2 單繼承問題拓展
- 3.3 重復調用問題
- 3.4 super(type) 問題
一、super( ) 的用途
了解 super() 函數之前,我們首先要知道 super() 的用途是啥?
- 主要用來在子類中調用父類的方法。
- 多用于多繼承問題中,解決查找順序(MRO)、重復調用(鉆石繼承)等種種問題。
二、了解 super 的基礎信息
語法格式:
super([type[, object-or-type]])
函數描述:
返回一個代理對象,它會將方法調用委托給 type 的父類或兄弟類。
參數說明:
type —— 類,可選參數。object-or-type —— 對象或類,一般是 self,可選參數。
返回值:
super object —— 代理對象。
help 幫助信息:
>>> help(super)
Help on class super in module builtins:
class super(object)
| super() -> same as super(__class__, first argument>)
| super(type) -> unbound super object
| super(type, obj) -> bound super object; requires isinstance(obj, type)
| super(type, type2) -> bound super object; requires issubclass(type2, type)
| Typical use to call a cooperative superclass method:
| class C(B):
| def meth(self, arg):
| super().meth(arg)
| This works for class methods too:
| class C(B):
| @classmethod
| def cmeth(cls, arg):
| super().cmeth(arg)
... ...
- super 是一個繼承自 object 的類,調用 super() 函數其實就是 super 類的實例化。
- 根據官方文檔的解釋 super() 函數返回的對象 —— super object,就是一個代理對象。
- super() 有四種參數的組合形式。
- super()適用于類的靜態方法。
三、典型用法
3.1 單繼承問題
首先我們看一個最基本的子類調用父類方法的示例:
>>> class A:
def funxx(self):
print("執行 A 中的 funxx 方法 ... ...")
>>> class B(A):
def funxx(self):
A.funxx(self) # 通過類名調用父類中的同名方法,self 參數代表 B 類的實例對象 b
print("執行 B 中的 funxx 方法 ... ...")
>>> b = B()
>>> b.funxx()
執行 A 中的 funxx 方法 ... ...
執行 B 中的 funxx 方法 ... ...
- 定義一個繼承自 A 類的子類 B,并在 B 類中重寫 funxx() 方法,B 中的 funxx() 是對 A 中的 funxx() 功能的拓展。
- 因為是拓展了 A 類的 funxx() 方法的功能,所以其任然保留了原功能,即要在子類 B 中調用父類的同名方法來實現原有功能。
- 上面的示例中是通過 A 類類名調用 A 類中的同名方法來實現的,而第一個參數 self 實際傳遞的是 B 類的實例 b。
使用 super() 函數來實現父類方法的調用:
>>> class A:
def funxx(self):
print("執行 A 中的 funxx 方法 ... ...")
>>> class B(A):
def funxx(self):
super().funxx()
print("執行 B 中的 funxx 方法 ... ...")
>>> b = B()
>>> b.funxx()
執行 A 中的 funxx 方法 ... ...
執行 B 中的 funxx 方法 ... ...
- 通過執行的結果可以看出實現了和普通類名調用的結果是一樣的。
- 在具有單繼承的類層級結構中,super 引用父類而不必顯式地指定它們的名稱,從而令代碼更易維護。(官方文檔描述)
- 也就是說,在子類中不再用父類名調用父類方法,而是用一個代理對象調用父類方法,這樣當父類名改變或者繼承關系發生變化時,不用對每個調用處都進行修改。
3.2 單繼承問題拓展
在 help()
的幫助信息中,也說明了類中使用 super()
不帶參數的形式等同于 super(__class__, first argument>)
這種形式。這也是 Python 2.x 和 Python 3.x 關于 super()
的區別。
改寫之前的單繼承問題的代碼:
>>> class A:
def funxx(self):
print("執行 A 中的 funxx 方法 ... ...")
>>> class B(A):
def funxx(self):
super(B, self).funxx()
print("執行 B 中的 funxx 方法 ... ...")
>>> b = B()
>>> b.funxx()
執行 A 中的 funxx 方法 ... ...
執行 B 中的 funxx 方法 ... ...
- 基本的調用方法
A.funxx(self)
,其中 self 指代實例對象 b。用語言描述為:實例對象 b 通過 A 類名調用方法 funxx()
。
- 官方描述:返回一個代理對象,它會將方法調用委托給 type 的父類或兄弟類。用語言描述為:代理對象 super 通過 type 的父類或兄弟類調用其中的方法。
- 我們發現 super 是通過參數設置來選擇調用哪個父類的方法。其中第二個參數給出 MRO(方法解析順序),也就是搜索目標方法的順序,第一個參數則給出搜索目標方法的范圍。
- 例如
super(B, self)
,第一個參數為 B,第二個參數 self 為實例 b,其所在類的繼承順序(MRO)為:B→A→object。所以調用時是在 B 的父類 A 中尋找,如找不到目標方法則會在更上一層的 object 中尋找。
示例:
class A:
pass
class B(A):
pass
class C(A):
def funxx(self):
print("找到 funxx() 位于 C 中...")
class D(A):
pass
class E(B, C):
pass
class F(E, D):
def funff(self):
print("執行 F 中的 funff()...")
super(E, self).funxx()
print(f"F 類的 MRO : {F.__mro__}")
f = F()
f.funff()
運行結果:
F 類的 MRO : (class '__main__.F'>, class '__main__.E'>, class '__main__.B'>, class '__main__.C'>, class '__main__.D'>, class '__main__.A'>, class 'object'>)
執行 F 中的 funff()...
找到 funxx() 位于 C 中...
- 我們可以看出 F 類的 MRO:F→E→B→C→D→A→object。
super()
函數的第一個參數為:E,目標是調用 E 類的父類 B 中的 funxx()
方法,可惜 B 類中沒找到,在 B 類的兄弟類 C 中找到了,符合要求。
3.3 重復調用問題
重復調用問題 也稱 鉆石繼承問題 或 菱形圖問題。
先來看看普通調用方法在:
>>> class A:
def __init__(self):
print("打印屬性 a")
>>> class B(A):
def __init__(self):
print("打印屬性 b")
A.__init__(self)
>>> class C(A):
def __init__(self):
print("打印屬性 c")
A.__init__(self)
>>> class D(B, C):
def __init__(self):
print("打印屬性 d")
B.__init__(self)
C.__init__(self)
>>> d = D()
打印屬性 d
打印屬性 b
打印屬性 a
打印屬性 c
打印屬性 a
- 因為 B,C 都繼承自 A,所以當 D 在實例化時,A 的構造函數被執行了兩次。這就是所謂的重復調用問題。
- 很顯然,我們只需要調用一次就可以了,重復的調用只會造成資源浪費。
接下來我們使用 super() 函數來調用:
>>> class A:
def __init__(self):
print("打印屬性 a")
>>> class B(A):
def __init__(self):
print("打印屬性 b")
super().__init__() # super() 等同于 super(B, self)
>>> class C(A):
def __init__(self):
print("打印屬性 c")
super().__init__() # super() 等同于 super(C, self)
>>> class D(B, C):
def __init__(self):
print("打印屬性 d")
super(D, self).__init__()
>>> d = D()
打印屬性 d
打印屬性 b
打印屬性 c
打印屬性 a
- 查看輸出結果我們發現雖然解決了重復調用問題,但是輸出結果的順序好像與我們想的有所區別。我們的慣性思維是:先執行 D 類的
__init__()
方法,接著調用 B 類的 __init__()
方法,B 類的構造方法中又調用了父類 A 的 __init_()
方法,然后再是調用 C 類的 __init_()
方法,該方法也調用了父類 A 的 __init__()
方法。所以執行的結果應該是:打印屬性 d,打印屬性 b,打印屬性 a,打印屬性 c。
- 為何結果不是我們想的那樣呢,首先我們要知道 D 類中的第二個參數 self 為 D 的實例 d,它提供的 MRO 為:D→B→C→A→object。所以 D 類中的
super()
函數產生的是 d 的代理對象,當其調用父類 B 的 __init__()
時,B 的 super()
的第二個參數為 D 中的 super object,其所提供的 MRO 依舊為:D→B→C→A→object。也就是說 B 中的 super()
調用的是它的上一級 C 中的 __init__()
,而不是 A 中的 __init__()
。所以執行的結果是:打印屬性 d,打印屬性 b,打印屬性 c,打印屬性 a。
3.4 super(type) 問題
>>> class A:
def funxx(self):
print("...A...")
>>> class B(A):
def funxx(self):
print("...B...")
>>> sa = super(B)
>>> print(sa)
super: class 'B'>, NULL>
>>> print(type(sa))
class 'super'>
可以看出 super(type) 返回的是一個無效的對象,或者是未綁定的 super object。
到此這篇關于Python super( )函數用法總結的文章就介紹到這了,更多相關super( )函數內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持腳本之家!
您可能感興趣的文章:- Python中super()函數簡介及用法分享
- Python中super函數的用法
- Python編程中對super函數的正確理解和用法解析
- 解決python super()調用多重繼承函數的問題
- 對Python3之方法的覆蓋與super函數詳解
- Python super()函數使用及多重繼承
- python super函數使用方法詳解
- python super()函數的基本使用
- Python中super函數用法實例分析
- python中super()函數的理解與基本使用