Python中super()函數(shù)簡(jiǎn)介及用法分享
首先看一下super()函數(shù)的定義:
super([type [,object-or-type]])
Return a **proxy object** that delegates method calls to a **parent or sibling** class of type.
返回一個(gè)代理對(duì)象, 這個(gè)對(duì)象負(fù)責(zé)將方法調(diào)用分配給第一個(gè)參數(shù)的一個(gè)父類或者同輩的類去完成.
parent or sibling class 如何確定?
第一個(gè)參數(shù)的__mro__屬性決定了搜索的順序, super指的的是 MRO(Method Resolution Order) 中的下一個(gè)類, 而不一定是父類!
super()和getattr() 都使用__mro__屬性來解析搜索順序, __mro__實(shí)際上是一個(gè)只讀的元組.
MRO中類的順序是怎么排的呢?
實(shí)際上MRO列表本身是根據(jù)一種C3的線性化處理技術(shù)確定的, 理論說明可以參考這里, 這里只簡(jiǎn)單說明一下原則:
在MRO中, 基類永遠(yuǎn)出現(xiàn)在派生類的后面, 如果有多個(gè)基類, 基類的相對(duì)順序不變.
MRO實(shí)際上是對(duì)繼承樹做層序遍歷的結(jié)果, 把一棵帶有結(jié)構(gòu)的樹變成了一個(gè)線性的表, 所以沿著這個(gè)列表一直往上, 就可以無重復(fù)的遍歷完整棵樹, 也就解決了多繼承中的Diamond問題.
比如說:
class Root: pass class A(Root): pass class B(Root): pass class C(A, B): pass print(C.__mro__) # 輸出結(jié)果為: # (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Root'>, <class 'object'>)
super()實(shí)際返回的是一個(gè)代理的super對(duì)象!
調(diào)用super()這個(gè)構(gòu)造方法時(shí), 只是返回一個(gè)super()對(duì)象, 并不做其他的操作.
然后對(duì)這個(gè)super對(duì)象進(jìn)行方法調(diào)用時(shí), 發(fā)生的事情如下:
找到第一個(gè)參數(shù)的__mro__列表中的下一個(gè)直接定義了該方法的類, 并實(shí)例化出一個(gè)對(duì)象
然后將這個(gè)對(duì)象的self變量綁定到第二個(gè)參數(shù)上, 返回這個(gè)對(duì)象
舉個(gè)例子:
class Root:
def __init__(self):
print('Root')
class A(Root):
def __init__(self):
super().__init__() # 等同于super(A, self).__init__()
在A的構(gòu)造方法中, 先調(diào)用super()得到一個(gè)super對(duì)象, 然后向這個(gè)對(duì)象調(diào)用init方法, 這是super對(duì)象會(huì)搜索A的__mro__列表, 找到第一個(gè)定義了__init__方法的類, 于是就找到了Root, 然后調(diào)用Root.__init__(self), 這里的self是super()的第二個(gè)參數(shù), 是編譯器自動(dòng)填充的, 也就是A的__init__的第一個(gè)參數(shù), 這樣就完成對(duì)__init__方法調(diào)用的分配.
注意: 在許多語言的繼承中, 子類必須調(diào)用父類的構(gòu)造方法, 就是為了保證子類的對(duì)象能夠填充上父類的屬性! 而不是初始化一個(gè)父類對(duì)象...(我之前就一直是這么理解的..). Python中就好多了, 所謂的調(diào)用父類構(gòu)造方法, 就是明明白白地把self傳給父類的構(gòu)造方法, 我的小身子骨就這么交給你了, 隨便你怎么折騰吧:joy:
參數(shù)說明
super() -> same as super(__class__, <first argument>) # <first argument>指的是調(diào)用super的函數(shù)的第一個(gè)參數(shù)
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)
如果提供了第二個(gè)參數(shù), 則找到的父類對(duì)象的self就綁定到這個(gè)參數(shù)上, 后面調(diào)用這個(gè)對(duì)象的方法時(shí), 可以自動(dòng)地隱式傳遞self.
如果第二個(gè)參數(shù)是一個(gè)對(duì)象, 則isinstance(obj, type)必須為True. 如果第二個(gè)參數(shù)為一個(gè)類型, 則issubclass(type2, type)必須為True
如果沒有傳遞第二個(gè)參數(shù), 那么返回的對(duì)象就是Unbound, 調(diào)用這個(gè)unbound對(duì)象的方法時(shí)需要手動(dòng)傳遞第一個(gè)參數(shù), 類似于Base.__int__(self, a, b).
不帶參數(shù)的super()只能用在類定義中(因?yàn)橐蕾囉赾aller的第二個(gè)參數(shù)), 編譯器會(huì)自動(dòng)根據(jù)當(dāng)前定義的類填充參數(shù).
也就是說, 后面所有調(diào)用super返回對(duì)象的方法時(shí), 第一個(gè)參數(shù)self都是super()的第二個(gè)參數(shù). 因?yàn)镻ython中所謂的方法, 就是一個(gè)第一個(gè)參數(shù)為self的函數(shù), 一般在調(diào)用方法的時(shí)候a.b()會(huì)隱式的將a賦給b()的第一個(gè)參數(shù).
super()的兩種常見用法:
單繼承中, super用來指代隱式指代父類, 避免直接使用父類的名字
多繼承中, 解決Diamond問題 (TODO)
對(duì)面向?qū)ο蟮睦斫?/p>
其實(shí)我覺得Python里面這樣的語法更容易理解面向?qū)ο蟮谋举|(zhì), 比Java中隱式地傳this更容易理解.
所謂函數(shù), 就是一段代碼, 接受輸入, 返回輸出. 所謂方法, 就是一個(gè)函數(shù)有了一個(gè)隱式傳遞的參數(shù). 所以方法就是一段代碼, 是類的所有實(shí)例共享的, 唯一不同的是各個(gè)實(shí)例調(diào)用的時(shí)候傳給方法的this 或者self不一樣而已.
構(gòu)造方法是什么呢? 其實(shí)也是一個(gè)實(shí)例方法啊, 它只有在對(duì)象生成了之后才能調(diào)用, 所以Python中__init__方法的參數(shù)是self啊. 調(diào)用構(gòu)造方法時(shí)其實(shí)已經(jīng)為對(duì)象分配了內(nèi)存, 構(gòu)造方法只是起到初始化的作用, 也就是為這段內(nèi)存里面賦點(diǎn)初值而已.
Java中所謂的靜態(tài)變量其實(shí)也就是類的變量, 其實(shí)也就是為類也分配了內(nèi)存, 里面存了這些變量, 所以Python中的類對(duì)象我覺得是很合理的, 也比Java要直觀. 至于靜態(tài)方法, 那就與對(duì)象一點(diǎn)關(guān)系都沒有了, 本質(zhì)就是個(gè)獨(dú)立的函數(shù), 只不過寫在了類里面而已. 而Python中的classmethod其實(shí)也是一種靜態(tài)方法, 不過它會(huì)依賴于cls對(duì)象, 這個(gè)cls就是類對(duì)象, 但是只要想用這個(gè)方法, 類對(duì)象必然是存在的, 不像實(shí)例對(duì)象一樣需要手動(dòng)的實(shí)例化, 所以classmethod也可以看做是一種靜態(tài)變量. 而staticmethod就是真正的靜態(tài)方法了, 是獨(dú)立的函數(shù), 不依賴任何對(duì)象.
Java中的實(shí)例方法是必須依賴于對(duì)象存在的, 因?yàn)橐[式的傳輸this, 如果對(duì)象不存在這個(gè)this也沒法隱式了. 所以在靜態(tài)方法中是沒有this指針的, 也就沒法調(diào)用實(shí)例方法. 而Python中的實(shí)例方法是可以通過類名來調(diào)用的, 只不過因?yàn)檫@時(shí)候self沒辦法隱式傳遞, 所以必須得顯式地傳遞.
相關(guān)文章
Python中關(guān)于面向?qū)ο笏接袑傩苑椒ǖ脑敿?xì)講解
在實(shí)際開發(fā)中,對(duì)象的某些屬性或方法可能只希望在對(duì)象的內(nèi)部被使用,而不希望在外部被訪問到,私有屬性就是對(duì)象不希望公開的屬性,私有方法就是對(duì)象不希望公開的方法2021-10-10
Python 實(shí)現(xiàn) T00ls 自動(dòng)簽到腳本代碼(郵件+釘釘通知)
這篇文章主要介紹了Python 實(shí)現(xiàn) T00ls 自動(dòng)簽到腳本(郵件+釘釘通知),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07
分析運(yùn)行中的 Python 進(jìn)程詳細(xì)解析
這篇文章主要介紹了分析運(yùn)行中的 Python 進(jìn)程,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-06-06
python實(shí)現(xiàn)簡(jiǎn)單加密解密機(jī)制
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)簡(jiǎn)單加密解密機(jī)制,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-03-03
Python MySQLdb模塊連接操作mysql數(shù)據(jù)庫實(shí)例
這篇文章主要介紹了Python MySQLdb模塊連接操作mysql數(shù)據(jù)庫實(shí)例,本文直接給出操作mysql代碼實(shí)例,包含創(chuàng)建表、插入數(shù)據(jù)、插入多條數(shù)據(jù)、查詢數(shù)據(jù)等內(nèi)容,需要的朋友可以參考下2015-04-04
Python實(shí)例一個(gè)類背后發(fā)生了什么
Python實(shí)例一個(gè)類背后發(fā)生了什么,本文為大家一一列出,感興趣的朋友可以參考一下2016-02-02
Python字符串轉(zhuǎn)換成浮點(diǎn)數(shù)函數(shù)分享
本文給大家分享的是一則使用Python實(shí)現(xiàn)字符串轉(zhuǎn)換成浮點(diǎn)數(shù)的代碼,主要是使用map和reduce方法來實(shí)現(xiàn),有需要的小伙伴可以參考下。2015-07-07
Python?HMAC模塊維護(hù)數(shù)據(jù)安全技術(shù)實(shí)例探索
本篇博客將帶領(lǐng)讀者深入探索Python中HMAC模塊的高級(jí)應(yīng)用,通過豐富的示例代碼和詳細(xì)的解釋,揭示HMAC在實(shí)際應(yīng)用場(chǎng)景中的多面光芒,從基礎(chǔ)概念到密碼存儲(chǔ)、文件完整性驗(yàn)證、API安全,再到與加密算法的巧妙結(jié)合2024-01-01
Python使用pyaudio實(shí)現(xiàn)錄音功能
pyaudio是一個(gè)跨平臺(tái)的音頻I/O庫,使用PyAudio可以在Python程序中播放和錄制音頻,本文將利用它實(shí)現(xiàn)錄音功能,并做到停止說話時(shí)自動(dòng)結(jié)束2023-05-05

