python super()作用和混用引发的问题

提示:本文是阅读书籍《Python 高级编程》的一些疑问梳理结果,用到了其中的某些示例


前言

编程语言中的一些功能都是为了解决某些问题或者提升编码性能而设计的,如果我们能了解这些功能解决背后解决的问题,那么对于我们的使用是很有帮助的,同时也能避免犯一些错误。


一、super()作用?

  1. super( )函数是用来调用父类的一个方法
    子类使用父类方法的三种方式:
    a. 直接继承,和父类的方法一样,缺点是没有特色
    b. 重写父类的方法,有特色,但是太麻烦,代码重复度较高
    c. super()方式继承父类的方式再扩展,相当于a+b的组合
  2. super( )函数解决多重继承的问题

二、super()混用

1.多重继承问题-混用super与显式类调用

代码如下(示例):

class A:
	def __init__(self):
		print("A", end=" ")
		super().__init__()
class B:
	def __init__(self):
		print("B", end=" ")
		super().__init__()
class C(A, B):
	def __init__(self):
	print("C", end=" ")
		A.__init__(self) # 显示调用
		B.__init__(self)

print("MRO:", [x.__name__ for x in C.__mro__])
#结果 MRO: ['C', 'A', 'B', 'object']
C()
C A B B 

为什么B会被调用两次?
出现以上这种情况的原因在于,C的实例调用了A.init(self),因此使得super(A, self).init()调用了B.init()方法。换
句话说,super应该被用到整个类的层次结构中。(普通程序员的我完全没有看懂,还是不理解)
可能对supper的理解有误?查了很久的资料:
super就是用来获取父类并用来调用父类方法的,这样说法其实是不对的,使用supper获取的不是父类,而是MRO列表中的下一个类,所谓MRO列表即方法解析顺序(Method Resolution Order)列表,它代表着类继承的顺序,我们可以使用以下几种获得某个类的MRO列表:

 C.mro() 
 C.__mro__ 
 c.__class__.__mro__ 

【参考:python中super()函数的理解与基本使用
从C的MRO列表中可以看出,C的实例调用了A.init(self),因此使得super(A, self).init()调用了B.init()方法,因为B在A的后面。

2.不同种类的参数

使用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, arg):
		print('base2')
		super().__init__()
class MyClass(Base1 , Base2):
	def __init__(self, arg):
		print('my base')
		super().__init__(arg)

尝试创建MyClass实例将会引发TypeError,原因是与父类的__init__()签名不匹配,三个类的函数签名如下所示:

Base1 OrderedDict([(‘self’, <Parameter “self”>)])
Base2 OrderedDict([(‘self’, <Parameter “self”>), (‘arg’, <Parameter “arg”>)])
CommonBase OrderedDict([(‘self’, <Parameter “self”>)])


怎么修改了:提供了四种解决方式:
a.所有的类__init__参数都是一样的

class CommonBase:
    def __init__(self,arg):
        print('CommonBase')
        super().__init__()
class Base1(CommonBase):
    def __init__(self, arg):
        print('Base1')
        super().__init__(arg)
class Base2(CommonBase):
    def __init__(self, arg):
        print('base2')
        super().__init__(arg)
class MyClass(Base1 , Base2):
    def __init__(self, arg):
        print('my base')
        print(arg)
        super().__init__(arg)

b. 修改继承顺序为Base2, Base1

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, arg):
   	print('base2')
   	super().__init__()
class MyClass( Base2, Base1 ):
   def __init__(self, arg):
   	print('my base')
   	super().__init__(arg)

c.书中的解决方式

class CommonBase:
   def __init__(self, *args, **kwargs):
   	print('CommonBase')
   	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)

一种解决方法是使用*args和**kwargs魔法包装的参数和关键字参数,这样即使不使用它们,所有的构造函数也会传递所有参数。安全性不高。
d.显示指定调用

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, arg):
       print('base2')
       super().__init__()
class MyClass(Base1 , Base2):
   def __init__(self, arg):
       print('my base')
       print(arg)
       Base2.__init__(self, arg)

总结

super()不仅仅是继承这么简单,更多的是和MRO相关,如果遇到解决不了的问题,可以从MRO方向思考一下。书中给了一些建议:

应该避免多重继承:可以采用第14章介绍的一些设计模式来代替它。
super的使用必须一致:在类的层次结构中,要么全部用super,要么全不用。混用super和传统调用是一种混乱的做法。人们往往会避免使用super,这样代码会更清晰。
**如果代码的使用范围包括Python 2,在Python 3中也应该显式地继承自object:**在Python 2中,没有指定任何祖先的类被认为是旧式类。在
Python 2中应避免混用旧式类和新式类。
调用父类时必须查看类的层次结构:为了避免出现任何问题,每次调用父类时,必须快速查看有关的MRO(使用__mro__)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
回答: Python中的super()函数主要用于在子类中调用父类的方法或属性。在类的初始化方法中,可以使用super()来调用父类的初始化方法,以便子类能够继承父类的属性和方法。通过使用super()函数,可以避免在子类中重复编写父类的初始化代码,提高代码的重用性和可维护性。除了在初始化方法中使用,super()函数还可以在子类的其他方法中调用父类的方法,实现子类对父类方法的扩展和重写。总之,super()函数在Python中是用来实现子类与父类之间的方法和属性的传递和调用的。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Python super( ) 函数详解](https://blog.csdn.net/qq_41961087/article/details/117674563)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [pythonsuper函数详解](https://blog.csdn.net/m0_58357681/article/details/128605941)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [彻底搞懂python super函数的作用](https://blog.csdn.net/taoqick/article/details/125021504)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值