*super函数(超类)

2024-02-11 17:30
文章标签 函数 super 超类

本文主要是介绍*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)

补充:

类方法重写的三种形式:

  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)的结果一样
什么都不写默认从最开始的类向后找。

这篇关于*super函数(超类)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/700391

相关文章

Kotlin 作用域函数apply、let、run、with、also使用指南

《Kotlin作用域函数apply、let、run、with、also使用指南》在Kotlin开发中,作用域函数(ScopeFunctions)是一组能让代码更简洁、更函数式的高阶函数,本文将... 目录一、引言:为什么需要作用域函数?二、作用域函China编程数详解1. apply:对象配置的 “流式构建器”最

Android Kotlin 高阶函数详解及其在协程中的应用小结

《AndroidKotlin高阶函数详解及其在协程中的应用小结》高阶函数是Kotlin中的一个重要特性,它能够将函数作为一等公民(First-ClassCitizen),使得代码更加简洁、灵活和可... 目录1. 引言2. 什么是高阶函数?3. 高阶函数的基础用法3.1 传递函数作为参数3.2 Lambda

C++中::SHCreateDirectoryEx函数使用方法

《C++中::SHCreateDirectoryEx函数使用方法》::SHCreateDirectoryEx用于创建多级目录,类似于mkdir-p命令,本文主要介绍了C++中::SHCreateDir... 目录1. 函数原型与依赖项2. 基本使用示例示例 1:创建单层目录示例 2:创建多级目录3. 关键注

C++中函数模板与类模板的简单使用及区别介绍

《C++中函数模板与类模板的简单使用及区别介绍》这篇文章介绍了C++中的模板机制,包括函数模板和类模板的概念、语法和实际应用,函数模板通过类型参数实现泛型操作,而类模板允许创建可处理多种数据类型的类,... 目录一、函数模板定义语法真实示例二、类模板三、关键区别四、注意事项 ‌在C++中,模板是实现泛型编程

kotlin的函数forEach示例详解

《kotlin的函数forEach示例详解》在Kotlin中,forEach是一个高阶函数,用于遍历集合中的每个元素并对其执行指定的操作,它的核心特点是简洁、函数式,适用于需要遍历集合且无需返回值的场... 目录一、基本用法1️⃣ 遍历集合2️⃣ 遍历数组3️⃣ 遍历 Map二、与 for 循环的区别三、高

C语言字符函数和字符串函数示例详解

《C语言字符函数和字符串函数示例详解》本文详细介绍了C语言中字符分类函数、字符转换函数及字符串操作函数的使用方法,并通过示例代码展示了如何实现这些功能,通过这些内容,读者可以深入理解并掌握C语言中的字... 目录一、字符分类函数二、字符转换函数三、strlen的使用和模拟实现3.1strlen函数3.2st

MySQL中COALESCE函数示例详解

《MySQL中COALESCE函数示例详解》COALESCE是一个功能强大且常用的SQL函数,主要用来处理NULL值和实现灵活的值选择策略,能够使查询逻辑更清晰、简洁,:本文主要介绍MySQL中C... 目录语法示例1. 替换 NULL 值2. 用于字段默认值3. 多列优先级4. 结合聚合函数注意事项总结C

Java8需要知道的4个函数式接口简单教程

《Java8需要知道的4个函数式接口简单教程》:本文主要介绍Java8中引入的函数式接口,包括Consumer、Supplier、Predicate和Function,以及它们的用法和特点,文中... 目录什么是函数是接口?Consumer接口定义核心特点注意事项常见用法1.基本用法2.结合andThen链

MySQL 日期时间格式化函数 DATE_FORMAT() 的使用示例详解

《MySQL日期时间格式化函数DATE_FORMAT()的使用示例详解》`DATE_FORMAT()`是MySQL中用于格式化日期时间的函数,本文详细介绍了其语法、格式化字符串的含义以及常见日期... 目录一、DATE_FORMAT()语法二、格式化字符串详解三、常见日期时间格式组合四、业务场景五、总结一、

golang panic 函数用法示例详解

《golangpanic函数用法示例详解》在Go语言中,panic用于触发不可恢复的错误,终止函数执行并逐层向上触发defer,最终若未被recover捕获,程序会崩溃,recover用于在def... 目录1. panic 的作用2. 基本用法3. recover 的使用规则4. 错误处理建议5. 常见错