*super函数(超类)

super()函数是用于调用父类(超类)的一个方法

super()是用来解决多继承问题的,直接用类名调用父类中的方法在单继承中不会出现问题,但是在多继承中会涉及到查找顺序(MRO),重复调用(钻石继承)等种种问题。
MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。

重复调用(钻石继承)代码演示:

class Parent(object):
    def __init__(self,name):
        print('parent的init开始被调用')
        self.name = name
        print('parent的init结束被调用')

class Son1(Parent):
    def __init__(self,name,age):
        print('Son1的init开始被调用')
        self.age = age
        Parent.__init__(self,name)
        print('Son1的init结束被调用')

class Son2(Parent):
    def __init__(self,name,gender):
        print('Son2的init开始被调用')
        self.gender = gender
        Parent.__init__(self,name)
        print('Son2的init结束被调用')

class Grandson(Son1,Son2):
    def __init__(self,name,age,gender):
        print('Grandsond的init开始被调用')
        Son1.__init__(self,name,age)    # 单独调用父类的初始化方法
        Son2.__init__(self,name,gender)
        print('Grandson的init结束被调用')

gs = Grandson('grandson',12,'男')
print('姓名',gs.name)
print('年龄',gs.age)
print('性别',gs.gender)

代码理解:

以上代码,子类调用父类中的方法书通过这种方式:
父类名.init(self,xxx) 例如:Parent.init(self,name)

1.过程:

首先整体上来看,这是多继承,Son1和Son2俩个类都继承了Parent类,Grandson类又继承了Son1和Son2俩个类。当实例化类的时候,会调用Grandson中的初始化方法__init__,但是现在要特别注意一件事,一定是先调用Grandson它自己的init方法,然后想法设法在它自己的init方法中调用上面的Son1,Son2,以及Parent中的init方法
然后思考,在Grandson中的init方法下又有Son1.init(self,name,age) 和 Son2.init(self,name,gender) ,注意,Python中的代码是从上往下来执行,因此,这里先执行Son1.init(self,name,age),而这中语法格式就是调用了Son1中的init方法,然后按照思路看Son1下的init方法,它的init方法下又调用了Parent的init方法:Parent.init(self,name),然后又执行Parent的init方法,执行的结果返回给Son1,然后又返回给Grandson,到此为止Son1.init(self,name,age)这句代码就执行完毕了。
而下面这句代码 Son2.init(self,name,gender),执行方式和Son1.init(self,name,age)一样,最终都要调用Parent的init方法。

配图:
在这里插入图片描述
2.代码评价:
说明一下这种格式:父类名.init(self,xxx) 的缺点
在实例化类的时候,传入了三个参数:‘grandson’,12,‘男’
然后看代码的执行结果:

Grandsond的init开始被调用
Son1的init开始被调用
parent的init开始被调用   *
parent的init结束被调用	*
Son1的init结束被调用
Son2的init开始被调用
parent的init开始被调用	*
parent的init结束被调用	*
Son2的init结束被调用
Grandson的init结束被调用
姓名 grandson
年龄 12
性别 男

注意我这里用*标记的代码,结果表明Parent类被重复调用了俩次。打个比方:孙子有三个吃的(三个参数),他的爸爸和他的大爷(Son1,Son2)也需要吃,然后他将吃的传给了他爸爸和他大爷,他爸爸和他大爷吃完发现他爷爷(Parent)也要吃,然后又先后传给了他爷爷,这样他爷爷既吃了他爸的,也吃了他大爷的,就是说他爷爷吃了俩遍,这里只有俩个儿子,所以就吃了俩遍,要是儿子多的话,他爷爷还不得吃死啊,所以最好每个人只吃一次就行。
所以说,Parent类最好被调用一次,要避免重复调用。

super()函数则能解决此问题,这里也要解释一下

MRO

再看如下代码:

class Parent(object):
    def __init__(self,name,*args,**kwargs): # 为避免多重继承报错
        print('parent的init开始被调用')
        self.name = name
        print('parent的init结束被调用')

class Son1(Parent):
    def __init__(self,name,age,*args,**kwargs):	# 为避免多重继承报错
        print('Son1的init开始被调用')
        self.age = age
        super().__init__(name,*args,**kwargs)
        print('Son1的init结束被调用')

class Son2(Parent):
    def __init__(self,name,gender,*args,**kwargs):	# 为避免多重继承报错
        print('Son2的init开始被调用')
        self.gender = gender
        super().__init__(name,*args,**kwargs)
        print('Son2的init结束被调用')

class Grandson(Son1,Son2):
    def __init__(self,name,age,gender):
        print('Grandsond的init开始被调用')
        super().__init__(name,age,gender)
        print('Grandson的init结束被调用')

print(Grandson.__mro__)

gs = Grandson('grandson',12,'男')
print('姓名',gs.name)
print('年龄',gs.age)
print('性别',gs.gender)

代码理解:

1.过程:
实例化的时候自动调用Grandson中的init方法,当代码执行到super的时候,会先找Son1中的init方法,当在Son1的init方法中执行到super时,注意,这里并不会去找Parent中的init方法,而是会去找和它同级的Son2中的init方法,当在Son2中的init方法中执行到super时,然后才会去找Parent中的init方法。

配图:
在这里插入图片描述
2.代码评价:
看代码执行的结果:

(<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)
Grandsond的init开始被调用
Son1的init开始被调用
Son2的init开始被调用
parent的init开始被调用	*
parent的init结束被调用	*
Son2的init结束被调用
Son1的init结束被调用
Grandson的init结束被调用
姓名 grandson
年龄 12
性别 男

我这里再次用*标记,结果表明Parent类只被调用了一次
用super函数明显避免了一个问题,那就是重复调用(钻石继承),最终Parent类只被调用了一次。效果明显!
这里再说明一下__mro__方法:
print(Grandson.mro)的执行结果就是整个代码类的继承顺序,方法解析顺序表是以一个元组的形式呈现
(<class ‘main.Grandson’>, <class ‘main.Son1’>, <class ‘main.Son2’>, <class ‘main.Parent’>, <class ‘object’>) 先是Grandson然后是Son1,然后是Son2,然后是Parent,最后是Object(Python3中默认继承的是object)

补充:

类方法重写的三种形式:

  1. 父类名.init(self,xxx,xxx):

  2. super().init(xxx,xxx)

  3. super(父类,self).init(xxx,xxx)

以上所有通过对比说明了前俩种方法,而第三种方法和第二种的区别我用MRO顺序表来说明:
就拿上方代码来说明,如果将Grandson类中的

super().__init__(name,age,gender)

改为

super(Son,self).__init__(name,age,gender)

当然结果会报错,但我就表明一下意思,就是说改了之后,按照MRO顺序表来看,他会直接找Parent,从而省略了中间很多索要继承的类,按照这个意思,super().init(name,age,gender)和super(Grandson,self).init(name,age,gender)的结果一样
什么都不写默认从最开始的类向后找。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值