python中的繼承機(jī)制super()函數(shù)詳解
前言
super 是用來解決多重繼承問題的,直接用類名調(diào)用父類方法在使用單繼承的時(shí)候沒問題
但是如果使用多繼承,會(huì)涉及到查找順序(MRO)、重復(fù)調(diào)用(鉆石繼承)等種種問題。
一、super用法
我們先簡(jiǎn)單的理解為super().xx相當(dāng)于調(diào)用了父類中的xx方法(實(shí)際上在單繼承中是這樣,多繼承中有點(diǎn)區(qū)別)。
時(shí)候會(huì)看到像下面這樣直接調(diào)用父類的一個(gè)方法:
class Base:
def __init__(self):
print('Base.__init__')
class A(Base):
def __init__(self):
Base.__init__(self)
print('A.__init__')盡管對(duì)于大部分代碼而言這么做沒什么問題,但是在更復(fù)雜的涉及到多繼承的代碼中就有可能導(dǎo)致很奇怪的問題發(fā)生。
比如,考慮如下的情況:
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__')
c = C()如果你運(yùn)行這段代碼就會(huì)發(fā)現(xiàn) Base.__init__() 被調(diào)用兩次,如下所示:
Base.__init__ A.__init__ Base.__init__ B.__init__ C.__init__
但是當(dāng)我們?cè)诖a中換成使用 super() ,結(jié)果就很完美了:
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__')運(yùn)行這個(gè)新版本后,你會(huì)發(fā)現(xiàn)每個(gè) __init__() 方法只會(huì)被調(diào)用一次了:
Base.__init__ B.__init__ A.__init__ C.__init__
二、super的本質(zhì)
先說說python中如何實(shí)現(xiàn)繼承---------對(duì)于你定義的每一個(gè)類,Python會(huì)計(jì)算出一個(gè)所謂的方法解析順序(MRO)列表。 這個(gè)MRO列表就是一個(gè)簡(jiǎn)單的所有基類的線性順序表。為了實(shí)現(xiàn)繼承,Python會(huì)在MRO列表上從左到右開始查找基類,直到找到第一個(gè)匹配這個(gè)屬性的類為止。
而這個(gè)MRO列表的構(gòu)造是通過一個(gè)C3線性化算法來實(shí)現(xiàn)的。 我們不去深究這個(gè)算法的數(shù)學(xué)原理,它實(shí)際上就是合并所有父類的MRO列表并遵循如下三條準(zhǔn)則:
- 子類會(huì)先于父類被檢查
- 多個(gè)父類會(huì)根據(jù)它們?cè)诹斜碇械捻樞虮粰z查
- 如果對(duì)下一個(gè)類存在兩個(gè)合法的選擇,選擇第一個(gè)父類
雖然名義上來說super是用來調(diào)用父類中的方法,但是super實(shí)際上是在MRO表中找到下一個(gè)匹配的類。super原型如下:
def super(cls, inst): mro = inst.__class__.mro() return mro[mro.index(cls) + 1]
兩個(gè)參數(shù) cls 和 inst 分別做了兩件事:
1. inst 負(fù)責(zé)生成 MRO 的 list
2. 通過 cls 定位當(dāng)前 MRO 中的 index, 并返回 mro[index + 1]
我們來看一個(gè)例子,猜猜下面的輸出會(huì)是什么呢:
class A():
def __init__(self):
print("Enter 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()直接看結(jié)果:

很多人將super簡(jiǎn)單的理解為調(diào)用父類中的方法,可能認(rèn)為應(yīng)該是D調(diào)用B和C,由于B在左邊,按順序先調(diào)用B,B油調(diào)用A,完成之后輪到D調(diào)用C,C調(diào)用A.輸出變成下面這樣:
Enter D
Enter B
Enter A
Leave B
Enter C
Enter A
Leave C
Leave D
但是根據(jù)我們上面說的super本質(zhì)知道super 和父類其實(shí)沒有實(shí)質(zhì)關(guān)聯(lián),我們就不難理解為什么 enter B 下一句是 enter C 而不是 enter A了(如果認(rèn)為 super 代表“調(diào)用父類的方法”,會(huì)想當(dāng)然的認(rèn)為下一句應(yīng)該是enter A)。流程如下,在 B 的 __init__ 函數(shù)中:
super(B,self).__init__() 首先獲取self.__class__.__mro__,但是這里的self是D的實(shí)例,而不是B的。
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
然后,通過 B 來定位 MRO 中的 index,并找到下一個(gè)。
顯然 B 的下一個(gè)是 C。于是,我們調(diào)用 C 的 __init__,打出 enter C。
當(dāng)你使用super()函數(shù)時(shí),Python會(huì)在MRO列表上繼續(xù)搜索下一個(gè)類。
只要每個(gè)重定義的方法統(tǒng)一使用super()并只調(diào)用它一次, 那么控制流最終會(huì)遍歷完整個(gè)MRO列表,每個(gè)方法也只會(huì)被調(diào)用一次。
到此這篇關(guān)于python中的繼承機(jī)制super()函數(shù)詳解的文章就介紹到這了,更多相關(guān)python繼承super()函數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python之Numpy的超實(shí)用基礎(chǔ)詳細(xì)教程
這篇文章主要介紹了Python之Numpy的超實(shí)用基礎(chǔ)詳細(xì)教程,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
PyChar學(xué)習(xí)教程之自定義文件與代碼模板詳解
pycharm默認(rèn)的【新建】文件,格式很不友好,那么就需要改一下文件模板。下面這篇文章主要給大家介紹了關(guān)于PyChar學(xué)習(xí)教程之自定義文件與代碼模板的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面跟著小編來一起看看吧。2017-07-07
django filter過濾器實(shí)現(xiàn)顯示某個(gè)類型指定字段不同值方式
這篇文章主要介紹了django filter過濾器實(shí)現(xiàn)顯示某個(gè)類型指定字段不同值方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-07-07
Python使用Spire.XLS進(jìn)行插入,修改和刪除迷你圖
這篇文章主要為大家詳細(xì)介紹如何使用 Spire.XLS for Python 在 Excel 中插入、修改和刪除迷你圖,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下2025-08-08
手把手教你打造個(gè)性化全棧應(yīng)用Python?Reflex框架全面攻略
Reflex框架是為了解決傳統(tǒng)全棧開發(fā)中的一些挑戰(zhàn)而誕生的,它充分利用了現(xiàn)代前端框架(如React)的優(yōu)勢(shì),與后端技術(shù)(如Node.js)深度集成,使得開發(fā)者能夠更加流暢地構(gòu)建整個(gè)應(yīng)用,Reflex的設(shè)計(jì)理念包括簡(jiǎn)化、響應(yīng)性和一致性,旨在提高全棧開發(fā)的效率和可維護(hù)性2023-12-12
用60行代碼實(shí)現(xiàn)Python自動(dòng)搶微信紅包
這篇文章主要介紹了用60行代碼實(shí)現(xiàn)Python自動(dòng)搶微信紅包,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02
Python functools模塊學(xué)習(xí)總結(jié)
這篇文章主要介紹了Python functools模塊學(xué)習(xí)總結(jié),本文講解了functools.partial、functool.update_wrapper、functool.wraps、functools.reduce、functools.cmp_to_key、functools.total_ordering等方法的使用實(shí)例,需要的朋友可以參考下2015-05-05

