码迷,mamicode.com
首页 > 其他好文 > 详细

类的继承中的super()

时间:2021-06-02 12:24:54      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:pytho   read   lis   -o   可见   error:   var   情况   word   

在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()

类的继承中的super()

标签:pytho   read   lis   -o   可见   error:   var   情况   word   

原文地址:https://www.cnblogs.com/qianfanwaer/p/14815626.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!