python中的super函式及MRO

2021-10-06 20:07:52 字數 3325 閱讀 8471

super() 函式是用於呼叫父類(超類)的乙個方法。

super是用來解決多重繼承問題的,直接用類名呼叫父類方法在使用單繼承的時候沒問題,但是如果使用多繼承,會涉及到查詢順序(mro)、重複呼叫(鑽石繼承)等種種問題。

此篇部落格所用直譯器為python3.x,不保證在其他版本直譯器下得到相同結果,也不打算討論早期python版本的mro機制。

呼叫父類(超類)方法的兩種方式:

類名.方法名

這種方式簡單易懂,但是無法處理多繼承的問題,多繼承是python物件導向技術的一項重要特性,為了利用好python的多繼承特性,我們應該找到更適合的方式來呼叫父類方法。

super函式.方法名

super函式呼叫父類方法有兩種形式,一種是super().父類方法,一種是super(c,obj).父類方法。其中第一種形式不用指定父類名和對應的例項化物件,使用起來更加靈活,後一種形式更加具體,所以也可以在**的其他地方使用。因為通過super函式呼叫時沒有類名,所以只能用於例項化方法。下面我們看一下**演示:

class a:

def go(self):

print('go a go')

class b:

def go(self):

print('go b go')

class c(a):

def go(self):

super().go()

print('go c go')

class d(b):

def go(self):

super().go()

print('go d go')

class e(a):

def go(self):

super().go()

print('go e go')

class f(c,e):

def go(self):

super().go()

print('go f go')

class g(f,d):

def go(self):

super().go()

print('go g go')

a = a()

b = b()

c = c()

d = d()

e = e()

f = f()

g = g()

讓我們看看呼叫各個類的例項化物件的go方法分別會輸出什麼:

in [33]: a.go()

go a go

in [34]: b.go()

go b go

in [35]: c.go()

go a go

go c go

in [36]: d.go()

go b go

go d go

in [37]: e.go()

go a go

go e go

目前為止我們對輸出的結果都能猜得到,再看最後兩個:

in [38]: f.go()

go a go

go e go

go c go

go f go

in [39]: g.go()

go a go

go e go

go c go

go f go

go g go

哎,我們還是再看點簡單的吧:

in [40]: super(g,g).go()

go a go

go e go

go c go

go f go

這個例子我們可以看到super函式的另外一種形式的使用。

python中的mro和c3演算法

mro就是類的方法解析順序表, 其實也就是繼承父類方法時的順序表。

我們可以先看一下相關的兩個概念

二義性:

python支援多繼承,多繼承的語言往往會遇到以下兩類二義性的問題:

1. 有兩個基類a和b,a和b都定義了方法f(),c繼承a和b,那麼呼叫c的f()方法時會出現不確定。

2. 有乙個基類a,定義了方法f(),b類和c類繼承了a類(的f()方法),d類繼承了b和c類,那麼出現乙個問題,d不知道應該繼承b的f()方法還是c的f()方法。

拓撲排序:

拓撲排序的實現步驟:

迴圈執行以下兩步,直到不存在入度為0的頂點為止

1. 選擇乙個入度為0的頂點並輸出之

2. 從網中刪除此頂點及所有出邊。

python中計算mro的c3演算法就利用了拓撲排序。我們可以畫出圖來看看類f和g中的繼承關係及mro是怎麼計算出來的。

上圖為f的繼承關係圖,f為最終子類,我們可以分析一下c3演算法中是怎麼處理f的繼承關係進而算出mro的。

首先找到入度為0的點,這裡是f,找到後在圖中刪除f及其對應的邊,這時入度為0的點有兩個,c和e,因為c的繼承順序在e之前,所以我們先處理c,到這裡為止繼承順序是fc,刪除掉c和它的邊之後,入度為0的點只有e了,到這裡為止繼承順序是fce,刪除掉e和它的邊之**度為0的點就剩a了,刪除a和它的所有邊,到這裡為止繼承順序是fcea,現在入度為0的點是object,刪除object和它的所有邊。繼承圖上面再沒有節點了,所以繼承順序的搜尋完成。最終的繼承順序是f->c->e->a->object。

再來分析一下g的繼承順序:

我們可以看到,c3演算法其實可以說是深度優先搜尋演算法的變種,它的向深度搜尋是有條件的,那就是某個點u向它可以直接到達的點v搜尋時v的入度應該為0。

知道了g的繼承順序之後我們分析一下g.go()的輸出結果。

呼叫g中的go函式時先呼叫f中的go函式,f中的go函式又去呼叫c中的go函式,c中的go函式又去呼叫e中的go函式,e中的go函式去呼叫a中的go函式,因為a中的go函式沒有super函式,所以函式呼叫棧結束,開始從上往下輸出結果。

類中的mro屬性

python的類中定義了mro函式,可以檢視類的mro,需要通過類名來呼叫。我們可以檢視f類和g類的mro:

in [41]: print(f.mro())

[, , , , ]

in [42]: print(g.mro())

[, , , , , , , ]

參考鏈結

python中的mro與多繼承

python中的super 函式

最能感受到super函式的作用在於進行鑽石繼承的時候。鑽石繼承 由乙個基類衍生兩個及以上的超類,然後在衍生,在類樹的最底層生成乙個子類,這樣的類樹結構就是乙個類似 鑽石外形,所以,最底層類繼承稱為鑽石繼承 首先 這是直接通過超類呼叫方法給子類使用 class baseclass num base c...

python中super函式的用法

super的正確傳參有兩種 super 類名,物件名 和super 類名1,類名2 來自wowcpp博主 引數必須要滿足的條件 super cls,obj 即傳入類名 物件名 obj物件必須是cls類的物件 cls子類的物件當然也是cls類的物件 記作 type obj cls 2.super cl...

python之類中的super函式

作用 實現 重用 思考 super真的只是呼叫父類麼?super函式是按照mro演算法去呼叫的,不bb上 class a def init self print a class b a def init self print b super init class c a def init self ...