python中super 函式的理解與基本使用

2022-09-25 05:36:11 字數 4542 閱讀 5595

目錄

python是一門物件導向的語言,定義類時經常要用到繼承,在類的繼承中,子類繼承父類中已經封裝好的方法,不需要再次編寫,如果子類如果重新定義了父類的某一方法,那麼該方法就會覆蓋父類的同名方法,但是有時我們希望子類保持父類程式設計客棧方法的基礎上進行擴充套件,而不是直接覆蓋,就需要先呼叫父類的方法,然後再進行功能的擴充套件,這時就可以通過super來實現對父類方法的呼叫。

看下面乙個例子:

class a:

def func(self):

print("a的func執行")

class b(a):

def func(self):

super().func()

print("b擴充套件的func執行")

b = b()

b.func()

# 輸出結果為:

# a的func執行

# b擴充套件的func執行

上面程式中,a是父類,b是a的子類,我程式設計客棧們在a類中重定義了func()方法,在b類中重新定義了func()方法,在方法中通過super().func()又呼叫了父類的方法,所以執行結果才會有a類func()方法輸出。

如果經常看python內建庫及第三方庫原始碼的話,你會發現,super用的非常多的地方是在子類中呼叫父類的初始化__init__()方法,這種用法非常常見。

class a:

def __init__(self, x):

self.x = x

class b(a):

def __init__(self, x, y):

super().__init__(x)

self.y = y

b = b(1, 2)

print(b.x, b.y)

看到這,你會想到super就是用來獲取父類並用來呼叫父類方法的,這樣說對不對呢,其實是不對的,使用supper獲取的不是父類,而是mro列表中的下乙個類,所謂mro列表即方法解析順序(method resolution order)列表,它代表著類繼承的順序,我們可以使用以下幾種獲得某個類的mro列表:

c.mro()

c.__mro__

c.__class__.__mro__

mro列表的順序確定經歷了很多次的變遷,最新的是通過c3線性化演算法來實現的,感興趣的話可以自行了解一下,總的來說,乙個類的mro列表就是合併所有父類的mro列表,並遵循以下三條原則:

下面來看一下下面這個例子:

class a(base):

def func(self):

print("a的func執行")

super().func()

print("a的func執行完畢")

class b(base):

def func(self):

print("b的func執行")

super().func()

print("b的func執行完畢")

class c(a, b):

def func(self):

print("c的func執行")

super().func()

print("c的func執行完畢")

c = c()

c.func()

# 獲取mro列表

print(c.__class__.__mro__)

執行結果如下:

上述程式中,base是父類,a、b都繼承自base,c繼承自 a、b,它們的繼承關係就是乙個典型的菱形繼承,如下:

通過結果我們可以看出,super並不是獲取父類並用來呼叫父類的方法,而是根據mro列表一次呼叫下乙個類,使用c.__class__.__mro__可以獲取mro列表,mro列表的順序是c、a、b、base、object。

super計算方法解析順序中的下乙個類,可以接收兩個引數:

def super(cls, inst):

mro = inst.__class__.mro()

return mro[mro.index(cls) + 1]

python 中,由於基類不會在 __init__() 中被隱式地呼叫,需要程式設計師顯式呼叫它們。這種情況下,當程式中包含多重繼承的類層次結構時,使用 super 是非常危險的,往往會在類的初始化過程**現問題。

分析如下程式,c 類使用了 __init__() 方法呼叫它的基類,會造成 b 類被呼叫了 2 次:

class a:

def __init__(self):

print("a",end=" ")

super().__init__()

class b:

def __init__(self):

print("b",end=" ")

super().__init__()

class c(a,b):

demtzkjof __init__(self):

print("c",end=" ")

a.__init__(self)

b.__init__(self)

print("mro:",[x.__name__ for x in c.__mro__])

c()執行結果為:

mro: ['c', 'a', 'b', 'object']

c a b b

出現以上這種情況的原因在於,c 的例項呼叫 a.__init__(self),使得 super(a,self).__init__() 呼叫了 b.__init__() 方法。換句話說,super 應該被用到整個類的層次結構中。

但是,有時這種層次結構的一部分位於第三方**中,我們無法確定外部包的這些**中是否使用 super(),因此,當需要對某個第三方類進行子類化時,最好檢視其內部**以及 mro 中其他類的內部**。

使用 super 的另乙個問題是初始化過程中的引數傳遞。如果沒有相同的簽名,乙個類怎麼能呼叫其基類的 __init__() **呢?這會導致下列問題:

class commonbase:

def __init__(self):

print("commonbase")

super().__init__()

class base1(commonbase):

def __init__(self):

print("base1")

super().__init__()

class base2(commonbase):

def __init__(self):

print("base2")

supmtzkjoer().__init__()

class myclass(base1,base2):

def __init__(self,arg):

print("my base")

super().__init__(arg)

myclass(10)

執行結果為:

my base

traceback (most recent call last):

file "c:\users\mengma\desktop\demo.py", line 20, in

myclass(10)

file "c:\users\mengma\desktop\demo.py", line 19, in __init__

super().__init__(arg)

typeerror: __init__() takes 1 positional argument but 2 were given

一種解決方法是使用 *args 和 **kwargs 包裝的引數和關鍵字引數,這樣即使不使用它們,所有的建構函式也會傳遞所有引數,如下所示:

class commonbase:

def __init__(self,*args,**kwargs):

print("com程式設計客棧monbase")

super().__init__()

class base1(commonbase):

def __init__(self,*args,**kwargs):

print("base1")

super().__init__(*args,**kwargs)

class base2(commonbase):

def __init__(self,*args,**kwargs):

print("base2")

super().__init__(*args,**kwargs)

class myclass(base1,base2):

def __init__(self,arg):

print("my base")

super().__init__(arg)

myclass(10)

執行結果為:

my base

base1

base2

commonbase

不過,這是一種很糟糕的解決方法,由於任何引數都可以傳入,所有建構函式都可以接受任何型別的引數,這會導致**變得脆弱。另一種解決方法是在 myclass 中顯式地使用特定類的 __init__() 呼叫,但這無疑會導致第一種錯誤。

英語好的話可以讀一下這邊文章python's super() considered super

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 ...