類的繼承中的super()


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

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

  1. 顯示調用父類的初始化函數,並且對屬性進行初始化;

  2. 通過super()初始化父類屬性;

對於方式1,代碼:

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,代碼:

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就被初始化了兩次。

另一個問題是,

 

而用super()雖然可以避免重復調用這個問題,但是在父類均有參數需要初始化時就很麻煩:

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 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/147692

 

因此,對於帶參數的多重繼承問題,使用super()會非常難用,不如使用顯示調用,帶來的小問題是部分類會重復初始化。

更進一步,在python中盡量不要使用多重繼承,會讓結構顯得非常復雜,代碼也變得脆弱。在單繼承場景中,則顯示調用或者super()都可以使用,注意,要么全部類都顯示調用,要么全部都用super()


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM