本文主要是介绍*super函数(超类),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
super()函数是用于调用父类(超类)的一个方法
super()是用来解决多继承问题的,直接用类名调用父类中的方法在单继承中不会出现问题,但是在多继承中会涉及到查找顺序(MRO),重复调用(钻石继承)等种种问题。
MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。
重复调用(钻石继承)代码演示:
class Parent(object):def __init__(self,name):print('parent的init开始被调用')self.name = nameprint('parent的init结束被调用')class Son1(Parent):def __init__(self,name,age):print('Son1的init开始被调用')self.age = ageParent.__init__(self,name)print('Son1的init结束被调用')class Son2(Parent):def __init__(self,name,gender):print('Son2的init开始被调用')self.gender = genderParent.__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 = nameprint('parent的init结束被调用')class Son1(Parent):def __init__(self,name,age,*args,**kwargs): # 为避免多重继承报错print('Son1的init开始被调用')self.age = agesuper().__init__(name,*args,**kwargs)print('Son1的init结束被调用')class Son2(Parent):def __init__(self,name,gender,*args,**kwargs): # 为避免多重继承报错print('Son2的init开始被调用')self.gender = gendersuper().__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)
补充:
类方法重写的三种形式:
-
父类名.init(self,xxx,xxx):
-
super().init(xxx,xxx)
-
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)的结果一样
什么都不写默认从最开始的类向后找。
这篇关于*super函数(超类)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!