super()方法详解
一、单独调用父类的方法
需求:编写一个类,然后再写一个子类进行继承,使用子类去调用父类的方法1。
使用方法1打印: 胖子老板,来包槟榔。
那么先写一个胖子老板的父类,执行一下:
class FatFather(object):
def __init__(self, name):
print('FatFather的init开始被调用')
self.name = name
print('FatFather的name是%s' % self.name)
print('FatFather的init调用结束')
def main():
ff = FatFather("胖子老板的父亲")
运行一下这个胖子老板父类的构造方法__init__ 如下:
if __name__ == "__main__":
main()
FatFather的init开始被调用
FatFather的name是胖子老板的父亲
FatFather的init调用结束
好了,那么下面来写一个子类,也就是胖子老板类,继承上面的类
# 胖子老板的父类
class FatFather(object):
def __init__(self, name):
print('FatFather的init开始被调用')
self.name = name
print('调用FatFather类的name是%s' % self.name)
print('FatFather的init调用结束')
# 胖子老板类 继承 FatFather 类
class FatBoss(FatFather):
def __init__(self, name, hobby):
print('胖子老板的类被调用啦!')
self.hobby = hobby
FatFather.__init__(self, name) # 直接调用父类的构造方法
print("%s 的爱好是 %s" % (name, self.hobby))
def main():
#ff = FatFather("胖子老板的父亲")
fatboss = FatBoss("胖子老板", "打斗地主")
在这上面的代码中,我使用FatFather.__init__(self,name)直接调用父类的方法。
运行结果如下:
if __name__ == "__main__":
main()
胖子老板的类被调用啦!
FatFather的init开始被调用
调用FatFather类的name是胖子老板
FatFather的init调用结束
胖子老板 的爱好是 打斗地主
二、super() 方法基本概念
除了直接使用 FatFather.__init__(self,name) 的方法,还可以使用super()方法来调用。
那么首先需要看super()方法的描述和语法理解一下super() 方法的使用。
2.1 描述
super() 函数是用于调用父类(超类)的一个方法。
super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。
MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。
2.2 语法
以下是 super() 方法的语法:
super(type[, object-or-type])
参数
type -- 类
object-or-type -- 类,一般是 self
Python3.x 和 Python2.x 的一个区别是: Python 3 可以使用直接使用 super().xxx 代替 super(Class, self).xxx :
- Python3.x 实例:
class A:
pass
class B(A):
def add(self, x):
super().add(x)
- Python2.x 实例:
class A(object): # Python2.x 记得继承 object
pass
class B(A):
def add(self, x):
super(B, self).add(x)
2.3 单继承使用super()
- 使用super() 方法来改写刚才胖子老板继承父类的 __init__ 构造方法
# 胖子老板的父类
class FatFather(object):
def __init__(self, name):
print('FatFather的init开始被调用')
self.name = name
print('调用FatFather类的name是%s' % self.name)
print('FatFather的init调用结束')
# 胖子老板类 继承 FatFather 类
class FatBoss(FatFather):
def __init__(self, name, hobby):
print('胖子老板的类被调用啦!')
self.hobby = hobby
#FatFather.__init__(self,name) # 直接调用父类的构造方法
super().__init__(name)
print("%s 的爱好是 %s" % (name, self.hobby))
def main():
#ff = FatFather("胖子老板的父亲")
fatboss = FatBoss("胖子老板", "打斗地主")
从上面使用super方法的时候,因为是单继承,直接就可以使用了。
运行如下:
if __name__ == "__main__":
main()
胖子老板的类被调用啦!
FatFather的init开始被调用
调用FatFather类的name是胖子老板
FatFather的init调用结束
胖子老板 的爱好是 打斗地主
那么为什么说单继承直接使用就可以呢?因为super()方法如果多继承的话,会涉及到一个MRO(继承父类方法时的顺序表) 的调用排序问题。
下面可以打印一下看看单继承的MRO顺序(FatBoss.mro)。
# 胖子老板的父类
class FatFather(object):
def __init__(self, name):
print('FatFather的init开始被调用')
self.name = name
print('调用FatFather类的name是%s' % self.name)
print('FatFather的init调用结束')
# 胖子老板类 继承 FatFather 类
class FatBoss(FatFather):
def __init__(self, name, hobby):
print('胖子老板的类被调用啦!')
self.hobby = hobby
#FatFather.__init__(self,name) # 直接调用父类的构造方法
super().__init__(name)
print("%s 的爱好是 %s" % (name, self.hobby))
def main():
print("打印FatBoss类的MRO")
print(FatBoss.__mro__)
print()
print("=========== 下面按照 MRO 顺序执行super方法 =============")
fatboss = FatBoss("胖子老板", "打斗地主")
上面的代码使用 FatBoss.__mro__ 可以打印出 FatBoss这个类经过 python解析器的 C3算法计算过后的继承调用顺序。
运行如下:
if __name__ == "__main__":
main()
打印FatBoss类的MRO
(<class '__main__.FatBoss'>, <class '__main__.FatFather'>, <class 'object'>)
=========== 下面按照 MRO 顺序执行super方法 =============
胖子老板的类被调用啦!
FatFather的init开始被调用
调用FatFather类的name是胖子老板
FatFather的init调用结束
胖子老板 的爱好是 打斗地主
从上面的结果 (<class '__main__.FatBoss'>, <class '__main__.FatFather'>, <class 'object'>) 可以看出,super() 方法在 FatBoss 会直接调用父类是 FatFather ,所以单继承是没问题的。
那么如果多继承的话,会有什么问题呢?
2.4 多继承使用super()
假设再写一个胖子老板的女儿类,和 胖子老板的老婆类,此时女儿需要同时继承 两个类(胖子老板类,胖子老板老婆类)。
因为胖子老板有一个爱好,胖子老板的老婆需要干活干家务,那么女儿需要帮忙同时兼顾。
此时女儿就是需要继承使用这两个父类的方法了,那么该如何去写呢?
下面来看看实现代码:
# 胖子老板的父类
class FatFather(object):
def __init__(self, name, *args, **kwargs):
print()
print("=============== 开始调用 FatFather ========================")
print('FatFather的init开始被调用')
self.name = name
print('调用FatFather类的name是%s' % self.name)
print('FatFather的init调用结束')
print()
print("=============== 结束调用 FatFather ========================")
# 胖子老板类 继承 FatFather 类
class FatBoss(FatFather):
def __init__(self, name, hobby, *args, **kwargs):
print()
print("=============== 开始调用 FatBoss ========================")
print('胖子老板的类被调用啦!')
#super().__init__(name)
## 因为多继承传递的参数不一致,所以使用不定参数
super().__init__(name, *args, **kwargs)
print("%s 的爱好是 %s" % (name, hobby))
print()
print("=============== 结束调用 FatBoss ========================")
# 胖子老板的老婆类 继承 FatFather类
class FatBossWife(FatFather):
def __init__(self, name, housework, *args, **kwargs):
print()
print("=============== 开始调用 FatBossWife ========================")
print('胖子老板的老婆类被调用啦!要学会干家务')
#super().__init__(name)
## 因为多继承传递的参数不一致,所以使用不定参数
super().__init__(name, *args, **kwargs)
print("%s 需要干的家务是 %s" % (name, housework))
print()
print("=============== 结束调用 FatBossWife ========================")
# 胖子老板的女儿类 继承 FatBoss FatBossWife类
class FatBossGril(FatBoss, FatBossWife):
def __init__(self, name, hobby, housework):
print('胖子老板的女儿类被调用啦!要学会干家务,还要会帮胖子老板斗地主')
super().__init__(name, hobby, housework)
def main():
print("打印FatBossGril类的MRO")
print(FatBossGril.__mro__)
print()
print("=========== 下面按照 MRO 顺序执行super方法 =============")
gril = FatBossGril("胖子老板", "打斗地主", "拖地")
运行结果如下:
if __name__ == "__main__":
main()
打印FatBossGril类的MRO
(<class '__main__.FatBossGril'>, <class '__main__.FatBoss'>, <class '__main__.FatBossWife'>, <class '__main__.FatFather'>, <class 'object'>)
=========== 下面按照 MRO 顺序执行super方法 =============
胖子老板的女儿类被调用啦!要学会干家务,还要会帮胖子老板斗地主
=============== 开始调用 FatBoss ========================
胖子老板的类被调用啦!
=============== 开始调用 FatBossWife ========================
胖子老板的老婆类被调用啦!要学会干家务
=============== 开始调用 FatFather ========================
FatFather的init开始被调用
调用FatFather类的name是胖子老板
FatFather的init调用结束
=============== 结束调用 FatFather ========================
胖子老板 需要干的家务是 拖地
=============== 结束调用 FatBossWife ========================
胖子老板 的爱好是 打斗地主
=============== 结束调用 FatBoss ========================
从上面的运行结果来看,我特意给每个类的调用开始以及结束都进行打印标识,可以看到。
每个类开始调用是根据MRO顺序进行开始,然后逐个进行结束的。
还有就是由于因为需要继承不同的父类,参数不一定。
所以,所有的父类都应该加上不定参数*args , **kwargs ,不然参数不对应是会报错的。
三、注意事项
super().__init__相对于类名.__init__,在单继承上用法基本无差
但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次,可以尝试写个代码来看输出结果
多继承时,使用super方法,对父类的传参数,应该是由于python中super的算法导致的原因,必须把参数全部传递,否则会报错
单继承时,使用super方法,则不能全部传递,只能传父类方法所需的参数,否则会报错
多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍, 而使用super方法,只需写一句话便执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因
四、练习
以下的代码的输出将是什么? 说出你的答案并解释。
class Parent(object):
x = 1
class Child1(Parent):
pass
class Child2(Parent):
pass
print(Parent.x, Child1.x, Child2.x)
1 1 1
Child1.x = 2
print(Parent.x, Child1.x, Child2.x)
1 2 1
- 注意:Child1已经拥有了属于自己的x
Parent.x = 3
print(Parent.x, Child1.x, Child2.x)
3 2 3
super()方法详解的更多相关文章
- Android ActionBar 关于tab的应用 以及 TabListener的方法详解
actionBar的tab标签应用以及TabListener的方法详解 package com.example.actionBarTest.actionBarTab; import android.a ...
- Clone使用方法详解【转载】
博客引用地址:Clone使用方法详解 Clone使用方法详解 java“指针” Java语言的一个优点就是取消了指针的概念,但也导致了许多程序员在编程中常常忽略了对象与引用的区别,本文 ...
- Python 在子类中调用父类方法详解(单继承、多层继承、多重继承)
Python 在子类中调用父类方法详解(单继承.多层继承.多重继承) by:授客 QQ:1033553122 测试环境: win7 64位 Python版本:Python 3.3.5 代码实践 ...
- Android——onCreate( )方法详解(转)
android开发之onCreate( )方法详解 onCreate( )方法是android应用程序中最常见的方法之一,那么,我们在使用onCreate()方法的时候应该注意哪些问题呢? 先看看Go ...
- Android编程之LayoutInflater的inflate方法详解
LayoutInflater的inflate方法,在fragment的onCreateView方法中经常用到: public View onCreateView(LayoutInflater infl ...
- 【Python】Linux crontab定时任务配置方法(详解)
CRONTAB概念/介绍 crontab命令用于设置周期性被执行的指令.该命令从标准输入设备读取指令,并将其存放于“crontab”文件中,以供之后读取和执行. cron 系统调度进程. 可以使用它在 ...
- ES6 类(Class)基本用法和静态属性+方法详解
原文地址:http://blog.csdn.net/pcaxb/article/details/53759637 ES6 类(Class)基本用法和静态属性+方法详解 JavaScript语言的传统方 ...
- java多线程并发(二)--线程的生命周期及方法详解
上篇随笔介绍了线程的相关基础知识以及新启线程的几种方法,本片将继续介绍线程的生命周期及方法详解. 一.线程的生命周期 在Thread代码中,线程的状态被分为6种 public enum State { ...
- 并发编程(六)Object类中线程相关的方法详解
一.notify() 作用:唤醒一个正在等待该线程的锁的线程 PS : 唤醒的线程不会立即执行,它会与其他线程一起,争夺资源 /** * Object类的notify()和notifyAll()方法详 ...
随机推荐
- 短信相关的AT指令以及信令
本文链接:https://blog.csdn.net/sjz4860402/article/details/78552756 此次的短信AT指令和信令从以下几个方面介绍: 一 . 短信AT指令的格式二 ...
- layui多个时间选择器出现闪退问题
1.出现问题的代码 laydate.render({ elem: '#startDate' // }); laydate.render({ elem: '#endDate' // }); laydat ...
- leetcode-10
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配. '.' 匹配任意单个字符'*' 匹配零个或多个前面的那一个元素所谓匹配,是要涵盖 整个 字符串 s的 ...
- 【搬了一套别人的cf】
自己打了一堆没保存瞬间全没了.... 没有继续写的欲望 https://www.cnblogs.com/tea-egg/p/11664350.html
- [译]Vulkan教程(05)Instance
[译]Vulkan教程(05)Instance Creating an instance 创建一个instance The very first thing you need to do is ini ...
- Oracle数据库的sql语句性能优化
在应用系统开发初期,由于开发数据库数据比较少,对于查询sql语句,复杂试图的编写等体会不出sql语句各种写法的性能优劣,但是如果将应用系统提交实际应用后,随着数据库中数据的增加,系统的响应速度就成为目 ...
- 查看SpringBoot应用中的嵌入式tomcat的版本
第一种,在启动springboot项目的时候,日志中可以看到 第二种,直接在maven依赖文件中查看 地址在:你的maven库文件夹/org/springframework/boot/spring-b ...
- ASP.NET Core 2.2 WebApi 系列【八】统一返回格式(返回值、模型验证、异常)
现阶段,基本上都是前后端分离项目,这样一来,就需要前后端配合,没有统一返回格式,那么对接起来会很麻烦,浪费时间.我们需要把所有接口及异常错误信息都返回一定的Json格式,有利于前端处理,从而提高了工作 ...
- Pycharm自带Git实现版本管理
之前一直使用本地的git客户端,通过命令来上传.下载代码到Gitlab:每次都需要启动git客户端,敲git命令来完成,不够灵活,因为强大的Pycharm就自带git功能,可以直接在Pycharm完成 ...
- 移动端H5页面遇到的问题总结(转载请注明出处)
最近刚做完一个移动端的项目,产品之无敌,过程之艰辛,我就不多说了,记录下在这个项目中遇到的问题,以防万一,虽然这些可能都是已经被N多前辈解决掉了的问题,也放在这里,算是为自己漫漫前端路铺了一颗小石子儿 ...