類的繼承中的super()

在python中,關於類的繼承有不少場景和知識點。今天聚焦在一個場景:有一個父類A,類中定義了某個問題中的通用屬性和方法(即後面的子類都須要用到),在子類B中須要繼承這些屬性和方法,同時添加本身特有的屬性和方法,應該如何實現?python

在子類中,繼承並初始化父類屬性的方式有兩種:函數

  1. 顯示調用父類的初始化函數,而且對屬性進行初始化;
  2. 經過super()初始化父類屬性;

對於方式1,代碼:ui

class A:
    def __init__(self,a,b):
        self.a=a
        self.b=b
    def func(self):
        return self.a+self.b

class B(A):
    def __init__(self,x1,x2,x3,x4):
        A.__init__(self,a=x1,b=x2)#注意必需要傳入self,至關於把B的實例傳進去
        self.c=x3
        self.d=x4
        self.m=A.func(self)#一樣必須傳入self
        self.n=self.func()
>>>ins=B(1,2,10,20)
>>>print(ins.a,ins.b,ins.c,ins.d)
1 2 10 20
>>>print(ins.m,ins.n)
3 3
>>>print(ins.func())
3

對於方式2,代碼:code

class A:
    def __init__(self,a,b):
        self.a=a
        self.b=b
    def func(self):
        return self.a+self.b

class B(A):
    def __init__(self,x1,x2,x3,x4):
        super().__init__(a=x1,b=x2)#初始化父類參數,注意不須要self了
        self.c=x3
        self.d=x4
        self.m=super().func()#super()一樣能調用父類的方法
        self.n=self.func()#也能夠直接調用父類方法
>>>ins=B(1,2,10,20)
>>>print(ins.a,ins.b,ins.c,ins.d)
1 2 10 20
>>>print(ins.m,ins.n)
3 3
>>>print(ins.func())
3

對於方式1,顯示調用進行初始化,在多重繼承的時候可能會出現重複調用的問題,如:繼承

class A:
    def __init__(self,a,b):
        self.a=a
        self.b=b
        print('AAAAA')
    def func(self):
        return self.a+self.b

class B(A):
    def __init__(self,x1,x2,x3,x4):
        A.__init__(self,a=x1,b=x2)
        self.c=x3
        self.d=x4
        print('BBBBB')

class C(A):
    def __init__(self,m,n,q):
        A.__init__(self,m,n)
        self.q=q
        print('CCCCC')

class D(B,C):
    def __init__(self,a,b,c,d,e,f):
        B.__init__(self,a,b,c,d)
        C.__init__(self,a,b,e)
        self.f=f
        print('DDDDDD')
>>>ins=D(1,2,3,4,5,6)
AAAAA
BBBBB
AAAAA
CCCCC
DDDDDD

能夠看到,A被調用了兩次。由於D繼承了B和C,在初始化B的時候,首先會初始化A,而後初始化B;在初始化C的時候,也會先初始化A,再初始化C;所以A就被初始化了兩次。get

另外一個問題是,it

而用super()雖然能夠避免重複調用這個問題,可是在父類均有參數須要初始化時就很麻煩:io

class A:
    def __init__(self):
        print('AAAAA')
    def func(self):
        return self.a+self.b

class B(A):
    def __init__(self):
        super().__init__()
        print('BBBBB')

class C(A):
    def __init__(self):
        super().__init__()
        print('CCCCC')

class D(B,C):
    def __init__(self):
        super().__init__()
        print('DDDDDD')
>>>ins=D()
AAAAA
CCCCC
BBBBB
DDDDDD
#可見,確實能夠避免重複調用的問題。

可是,若是父類均有參數,那麼這個時候問題就很大了,如:class

class A:
    def __init__(self,a,b):
        self.a=a
        self.b=b
        print('AAAAA')
    def func(self):
        return self.a+self.b

class B(A):
    def __init__(self,x1,x2,x3,x4):
        super().__init__(x1,x2)
        self.c=x3
        self.d=x4
        print('BBBBB')

class C(A):
    def __init__(self,m,n,q):
        super().__init__(m,n)
        self.q=q
        print('CCCCC')

class D(B,C):
    def __init__(self,a,b,c,d,e,f):
        super().__init__(a,b,c,d)##這種狀況下,由於是按照MRO法則來以此對全部父類進行初始化,所以這裏不管怎麼傳參數,都是有可能報錯的。由於不一樣父類的入參不一樣。根據後面查看的MRO順序,調用順序是D-B-C-A,所以這裏初始化的時候IDE會提示輸入4個參數,由於首先調用的是B,而B的初始化須要4個參數。

        self.f=f
        print('DDDDDD')
        
>>>ins=D(1,2,3,4,5,6)

TypeError: __init__() missing 1 required positional argument: 'q'
        
>>>print(D.__mro__)#查看D的父類調用MRO順序
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
#所以。能夠看到,首先調用的是B,初始化成功;而在初始化C的時候報錯,C中的m,n都是初始化A,而A已經在前一步初始化過了。可是由於全部的參數都進了B,如今初始化C的時候缺一個參數q,所以報錯。

對於帶參數的多重繼承問題,另外一個一樣的例子能夠參考來理解:https://www.pythonf.cn/read/1...require

所以,對於帶參數的多重繼承問題,使用super()會很是難用,不如使用顯示調用,帶來的小問題是部分類會重複初始化。

更進一步,在python中儘可能不要使用多重繼承,會讓結構顯得很是複雜,代碼也變得脆弱。在單繼承場景中,則顯示調用或者super()均可以使用,注意,要麼所有類都顯示調用,要麼所有都用super()

相關文章
相關標籤/搜索