Python中super函數(shù)用法實(shí)例分析
本文實(shí)例講述了Python中super函數(shù)用法。分享給大家供大家參考,具體如下:
這是個(gè)高大上的函數(shù),在python裝13手冊(cè)里面介紹過(guò)多使用可顯得自己是高手 23333. 但其實(shí)他還是很重要的. 簡(jiǎn)單說(shuō), super
函數(shù)是調(diào)用下一個(gè)父類(超類)并返回該父類實(shí)例的方法. 這里的下一個(gè)的概念參考后面的MRO表介紹.
help介紹如下:
super(type, obj) -> bound super object; requires isinstance(obj, type)
super(type) -> unbound super object
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(C, self).meth(arg)
由此可知, super有三種用法, 第一參數(shù)總是召喚父類的那個(gè)類, 第二參數(shù)可缺(返回非綁定父類對(duì)象),也可以是實(shí)例對(duì)象或該類的子類. 最終返回的都是父類的實(shí)例(綁定或非綁定). 在Python3中,super函數(shù)多了一種用法是直接super()
,相當(dāng)于super(type,首參)
, 這個(gè)首參就是一般的傳入的self
實(shí)例本身啦. 因?yàn)樵趐y2里面常用也是這種寫法.
另外, 在py2中, super只支持新類( new-style class, 就是繼承自object的).
為什么要調(diào)用父類?
在類繼承時(shí), 要是重定義某個(gè)方法, 這個(gè)方法就會(huì)覆蓋掉父類的相應(yīng)同名方法. 通過(guò)調(diào)用父類實(shí)例, 可以在子類中同時(shí)實(shí)現(xiàn)父類的功能.例如:
# Should be new-class based on object in python2. class A(object): def __init__(self): print "enter A" print "leave A" class B(A): def __init__(self): print "enter B" super(B, self).__init__() print "leave B" >>> b = B() enter B enter A leave A leave B
通過(guò)調(diào)用super
獲得父類實(shí)例從而可以實(shí)現(xiàn)該實(shí)例的初始化函數(shù). 這在實(shí)踐中太常用了 (因?yàn)橐^承父類的功能, 又要有新的功能).
直接使用父類來(lái)調(diào)用的差異
事實(shí)上, 上面的super
函數(shù)方法還可以這么寫:
class A(object): def __init__(self): print "enter A" print "leave A" class B(A): def __init__(self): print "enter B" A.__init__(self) print "leave B"
通過(guò)直接使用父類類名來(lái)調(diào)用父類的方法, 實(shí)際也是可行的. 起碼在上面的例子中效果上他們現(xiàn)在是一樣的. 這種方法在老式類中也是唯一的調(diào)用父類的方法 (老式類沒(méi)有super).
通過(guò)父類類名調(diào)用方法很常用, 比較直觀. 但其效果和super還是有差異的. 例如:
class A(object): def __init__(self): print "enter A" print "leave A" class B(A): def __init__(self): print "enter B" A.__init__(self) print "leave B" class C(A): def __init__(self): print "enter C" A.__init__(self) print "leave C" class D(B,C): def __init__(self): print "enter D" B.__init__(self) C.__init__(self) print "leave D" >>> d=D() enter D enter B enter A leave A leave B enter C enter A leave A leave C leave D
可以發(fā)現(xiàn), 這里面A的初始化函數(shù)被執(zhí)行了兩次. 因?yàn)槲覀兺瑫r(shí)要實(shí)現(xiàn)B和C的初始化函數(shù), 所以分開(kāi)調(diào)用兩次, 這是必然的結(jié)果.
但如果改寫成super呢?
class A(object): def __init__(self): print "enter A" print "leave A" class B(A): def __init__(self): print "enter B" super(B,self).__init__() print "leave B" class C(A): def __init__(self): print "enter C" super(C,self).__init__() print "leave C" class D(B,C): def __init__(self): print "enter D" super(D,self).__init__() print "leave D" >>> d=D() enter D enter B enter C enter A leave A leave C leave B leave D
會(huì)發(fā)現(xiàn)所有父類ABC只執(zhí)行了一次, 并不像之前那樣執(zhí)行了兩次A的初始化.
然后, 又發(fā)現(xiàn)一個(gè)很奇怪的: 父類的執(zhí)行是 BCA 的順序并且是全進(jìn)入后再統(tǒng)一出去. 這是MRO表問(wèn)題, 后面繼續(xù)討論.
如果沒(méi)有多繼承, super
其實(shí)和通過(guò)父類來(lái)調(diào)用方法差不多. 但, super還有個(gè)好處: 當(dāng)B繼承自A, 寫成了A.__init__
, 如果根據(jù)需要進(jìn)行重構(gòu)全部要改成繼承自 E
,那么全部都得改一次! 這樣很麻煩而且容易出錯(cuò)! 而使用super()
就不用一個(gè)一個(gè)改了(只需類定義中改一改就好了)
Anyway, 可以發(fā)現(xiàn), super
并不是那么簡(jiǎn)單.
MRO 表
MRO是什么? 可以通過(guò)以下方式調(diào)出來(lái):
>>> D.mro() # or d.__class__.mro() or D.__class__.mro(D) [D, B, C, A, object] >>> B.mro() [B, A, object] >>> help(D.mro) #Docstring: #mro() -> list #return a type's method resolution order #Type: method_descriptor
MRO就是類的方法解析順序表, 其實(shí)也就是繼承父類方法時(shí)的順序表 (類繼承順序表去理解也行) 啦.
這個(gè)表有啥用? 首先了解實(shí)際super做了啥:
def super(cls, inst): mro = inst.__class__.mro() return mro[mro.index(cls) + 1]
換而言之, super方法實(shí)際是調(diào)用了cls的在MRO表中的下一個(gè)類. 如果是簡(jiǎn)單一條線的單繼承, 那就是父類->父類一個(gè)一個(gè)地下去羅. 但對(duì)于多繼承, 就要遵循MRO表中的順序了. 以上面的D的調(diào)用為例:
d的初始化
-> D (進(jìn)入D) super(D,self)
-> 父類B (進(jìn)入B) super(B,self)
-> 父類C (進(jìn)入C) super(C,self)
-> 父父類A (進(jìn)入A) (退出A) # 如有繼續(xù)super(A,self) -> object (停了)
-> (退出C)
-> (退出B)
-> (退出D)
所以, 在MRO表中的超類初始化函數(shù)只執(zhí)行了一次!
那么, MRO的順序究竟是怎么定的呢? 這個(gè)可以參考官方說(shuō)明The Python 2.3 Method Resolution Order . 基本就是, 計(jì)算出每個(gè)類(從父類到子類的順序)的MRO, 再merge 成一條線. 遵循以下規(guī)則:
在 MRO 中,基類永遠(yuǎn)出現(xiàn)在派生類后面,如果有多個(gè)基類,基類的相對(duì)順序保持不變。 這個(gè)原則包括兩點(diǎn):
1. 基類永遠(yuǎn)在派生類后面
2. 類定義時(shí)的繼承順序影響相對(duì)順序.
如果有以下繼承 (Python中的super用法詳解):
object
/ \
/ A
| / \
B-1 C-2 D-2
\ / /
E-1 /
\ /
F
那么MRO是: F -> E -> B -> C -> D -> A -> object
怎么解釋呢?
根據(jù)官方的方法, 是:
L(O) = O L(B) = B O L(A) = A O L(C) = C A O L(D) = D A O L(E) = E + merge(L(B),L(C)) = E + merge(BO,CAO) = E + B + merge(O,CAO) = E + B + C + merge(O,AO) = E + B + C + A + merge(O,O) = E B C A O L(F) = F + merge(L(E),L(D)) = F + merge(EBCAO,DAO) = F + EBC + merge(AO,DAO) = F + EBC + D + merge(AO,AO) = F EBC D AO
看起來(lái)很復(fù)雜..但還是遵循在 MRO 中,基類永遠(yuǎn)出現(xiàn)在派生類后面,如果有多個(gè)基類,基類的相對(duì)順序保持不變。所以, 我個(gè)人認(rèn)為可以這么想:
- 先找出最長(zhǎng)深度最深的繼承路線
F->E->C->A->object
. (因?yàn)楸厝?strong>基類永遠(yuǎn)出現(xiàn)在派生類后面) - 類似深度優(yōu)先, 定出其余順序:
F->E->B->obj
,F->D->A-object
- 如果有多個(gè)基類,基類的相對(duì)順序保持不變, 類似于merge時(shí)優(yōu)先提前面的項(xiàng). 所以排好這些路線: (FEBO, FECAO, FDAO)
F->E->B->obj
且E(B,C)決定B在C前面.所以F->E->B->C->A->obj
(相當(dāng)于F+merge(EBO,ECAO)
).F->D->A-object
且F(E,D)決定了D在E后, 所以D在E后A前. 因?yàn)橄鄬?duì)順序, 相當(dāng)于FE+merge(BCAO, DAO)
, 所以FE BC D AO
更多可參考
- Raymond Hettinger 的 (據(jù)說(shuō)很經(jīng)典的討論)
- James Knight 的
- Py3 cookbook: 8.7 調(diào)用父類方法http://python3-cookbook.readthedocs.org/zh_CN/latest/c08/p07_calling_method_on_parent_class.html
- Python編程中對(duì)super函數(shù)的正確理解和用法解析
關(guān)于Python相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Python面向?qū)ο蟪绦蛟O(shè)計(jì)入門與進(jìn)階教程》、《Python數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Python函數(shù)使用技巧總結(jié)》、《Python字符串操作技巧匯總》、《Python編碼操作技巧總結(jié)》及《Python入門與進(jìn)階經(jīng)典教程》
希望本文所述對(duì)大家Python程序設(shè)計(jì)有所幫助。
- Python中super()函數(shù)簡(jiǎn)介及用法分享
- Python中super函數(shù)的用法
- Python編程中對(duì)super函數(shù)的正確理解和用法解析
- 解決python super()調(diào)用多重繼承函數(shù)的問(wèn)題
- 對(duì)Python3之方法的覆蓋與super函數(shù)詳解
- Python super()函數(shù)使用及多重繼承
- python super函數(shù)使用方法詳解
- python super()函數(shù)的基本使用
- Python super( )函數(shù)用法總結(jié)
- python中super()函數(shù)的理解與基本使用
相關(guān)文章
python語(yǔ)音識(shí)別實(shí)踐之百度語(yǔ)音API
這篇文章主要為大家詳細(xì)介紹了python語(yǔ)音識(shí)別實(shí)踐之百度語(yǔ)音API,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08python 將數(shù)據(jù)保存為excel的xls格式(實(shí)例講解)
下面小編就為大家分享一篇python 將數(shù)據(jù)保存為excel的xls格式(實(shí)例講解),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-05-05python處理二進(jìn)制數(shù)據(jù)的方法
這篇文章主要介紹了python處理二進(jìn)制數(shù)據(jù)的方法,涉及Python針對(duì)二進(jìn)制數(shù)據(jù)的相關(guān)操作技巧,需要的朋友可以參考下2015-06-06Python3二分查找?guī)旌瘮?shù)bisect(),bisect_left()和bisect_right()的區(qū)別
這篇文章主要介紹了Python3二分查找?guī)旌瘮?shù)bisect(),bisect_left()和bisect_right()的區(qū)別,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03python3.6 實(shí)現(xiàn)AES加密的示例(pyCryptodome)
本篇文章主要介紹了python3.6 實(shí)現(xiàn)AES加密的示例(pyCryptodome),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-01Python中的startswith和endswith函數(shù)使用實(shí)例
這篇文章主要介紹了Python中的startswith和endswith函數(shù)使用實(shí)例,特別是endswith函數(shù),有了它,判斷文件的擴(kuò)展名、文件的類型在容易不過(guò)了,需要的朋友可以參考下2014-08-08Python開(kāi)啟線程,在函數(shù)中開(kāi)線程的實(shí)例
今天小編就為大家分享一篇Python開(kāi)啟線程,在函數(shù)中開(kāi)線程的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-02-02windows中安裝Python3.8.0的實(shí)現(xiàn)方法
這篇文章主要介紹了windows中安裝Python3.8.0的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11