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