Python多继承以及MRO顺序的使用


Posted in Python onNovember 11, 2019

多继承以及MRO顺序

1. 单独调用父类的方法

# coding=utf-8

print("******多继承使用类名.__init__ 发生的状态******")
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('Grandson的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)

print("******多继承使用类名.__init__ 发生的状态******\n\n")

运行结果:

******多继承使用类名.__init__ 发生的状态******
Grandson的init开始被调用
Son1的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son1的init结束被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Grandson的init结束被调用
姓名: grandson
年龄: 12
性别: 男
******多继承使用类名.__init__ 发生的状态******

2. 多继承中super调用有所父类的被重写的方法

print("******多继承使用super().__init__ 发生的状态******")
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('Grandson的init开始被调用')
  # 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍
  # 而super只用一句话,执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因
  # super(Grandson, self).__init__(name, age, gender)
  super().__init__(name, age, gender)
  print('Grandson的init结束被调用')

print(Grandson.__mro__)

gs = Grandson('grandson', 12, '男')
print('姓名:', gs.name)
print('年龄:', gs.age)
print('性别:', gs.gender)
print("******多继承使用super().__init__ 发生的状态******\n\n")

运行结果:

******多继承使用super().__init__ 发生的状态******
(<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)
Grandson的init开始被调用
Son1的init开始被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Son1的init结束被调用
Grandson的init结束被调用
姓名: grandson
年龄: 12
性别: 男
******多继承使用super().__init__ 发生的状态******

注意:

1.  以上2个代码执行的结果不同
2.  如果2个子类中都继承了父类,当在子类中通过父类名调用时,parent被执行了2次
3.  如果2个子类中都继承了父类,当在子类中通过super调用时,parent被执行了1次

3. 单继承中super

print("******单继承使用super().__init__ 发生的状态******")
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
  super().__init__(name) # 单继承不能提供全部参数
  print('Son1的init结束被调用')

class Grandson(Son1):
 def __init__(self, name, age, gender):
  print('Grandson的init开始被调用')
  super().__init__(name, age) # 单继承不能提供全部参数
  print('Grandson的init结束被调用')

gs = Grandson('grandson', 12, '男')
print('姓名:', gs.name)
print('年龄:', gs.age)
#print('性别:', gs.gender)
print("******单继承使用super().__init__ 发生的状态******\n\n")

总结

  1. super().__init__相对于类名.init,在单继承上用法基本无差
  2. 但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次,具体看前面的输出结果
  3. 多继承时,使用super方法,对父类的传参数,应该是由于python中super的算法导致的原因,必须把参数全部传递,否则会报错
  4. 单继承时,使用super方法,则不能全部传递,只能传父类方法所需的参数,否则会报错
  5. 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍,
  6. 而使用super方法,只需写一句话便执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因

小试牛刀(以下为面试题)

以下的代码的输出将是什么? 说出你的答案并解释。

class Parent(object):
 x = 1

class Child1(Parent):
 pass

class Child2(Parent):
 pass

print(Parent.x, Child1.x, Child2.x)
Child1.x = 2
print(Parent.x, Child1.x, Child2.x)
Parent.x = 3
print(Parent.x, Child1.x, Child2.x)

答案, 以上代码的输出是:

使你困惑或是惊奇的是关于最后一行的输出是 3 2 3 而不是 3 2 1。为什么改变了 Parent.x 的值还会改变 Child2.x
的值,但是同时 Child1.x 值却没有改变?

这个答案的关键是,在 Python中,类变量在内部是作为字典处理的。如果一个变量的名字没有在当前类的字典中发现,将搜索祖先类(比如父类)直到被引用的变量名被找到(如果这个被引用的变量名既没有在自己所在的类又没有在祖先类中找到,会引发一个 AttributeError 异常 )。

因此,在父类中设置 x = 1 会使得类变量 x 在引用该类和其任何子类中的值为 1。这就是因为第一个 print 语句的输出是 1 1 1。
随后,如果任何它的子类重写了该值(例如,我们执行语句 Child1.x = 2),然后,该值仅仅在子类中被改变。这就是为什么第二个print 语句的输出是 1 2 1。

最后,如果该值在父类中被改变(例如,我们执行语句 Parent.x =
3),这个改变会影响到任何未重写该值的子类当中的值(在这个示例中被影响的子类是 Child2)。这就是为什么第三个 print 输出是 3 2 3。

 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python中的测试模块unittest和doctest的使用教程
Apr 14 Python
python3+PyQt5实现自定义流体混合窗口部件
Apr 24 Python
对Python实现累加函数的方法详解
Jan 23 Python
获取django框架orm query执行的sql语句实现方法分析
Jun 20 Python
django 多对多表的创建和插入代码实现
Sep 09 Python
python单向链表的基本实现与使用方法【定义、遍历、添加、删除、查找等】
Oct 24 Python
解决TensorFlow训练内存不断增长,进程被杀死问题
Feb 05 Python
Python全面分析系统的时域特性和频率域特性
Feb 26 Python
python 实现人和电脑猜拳的示例代码
Mar 02 Python
PyCharm Anaconda配置PyQt5开发环境及创建项目的教程详解
Mar 24 Python
sklearn的predict_proba使用说明
Jun 28 Python
详解如何在pyqt中通过OpenCV实现对窗口的透视变换
Sep 20 Python
python 有效的括号的实现代码示例
Nov 11 #Python
Python+OpenCV实现实时眼动追踪的示例代码
Nov 11 #Python
python的pyecharts绘制各种图表详细(附代码)
Nov 11 #Python
python OpenCV GrabCut使用实例解析
Nov 11 #Python
Python上下文管理器用法及实例解析
Nov 11 #Python
Django 请求Request的具体使用方法
Nov 11 #Python
浅谈Python类中的self到底是干啥的
Nov 11 #Python
You might like
使用PHP提取视频网站页面中的FLASH地址的代码
2010/04/17 PHP
Java中final关键字详解
2015/08/10 PHP
Laravel Intervention/image图片处理扩展包的安装、使用与可能遇到的坑详解
2017/11/14 PHP
thinkPHP框架实现的短信接口验证码功能示例
2018/06/20 PHP
javascript hasFocus使用实例
2010/06/29 Javascript
原生js的弹出层且其内的窗口居中
2014/05/14 Javascript
jQuery超简单选项卡完整实例
2015/09/26 Javascript
jQuery插件实现带圆点的焦点图片轮播切换
2016/01/18 Javascript
在web中js实现类似excel的表格控件
2016/09/01 Javascript
Node.js用readline模块实现输入输出
2016/12/16 Javascript
详解vue-cli 构建Vue项目遇到的坑
2017/08/30 Javascript
VSCode配置react开发环境的步骤
2017/12/27 Javascript
bmob js-sdk 在vue中的使用教程
2018/01/21 Javascript
vue中v-cloak解决刷新或者加载出现闪烁问题(显示变量)
2018/04/20 Javascript
js 图片转base64的方式(两种)
2018/04/24 Javascript
vue中keep-alive的用法及问题描述
2018/05/15 Javascript
vue如何引入sass全局变量
2018/06/28 Javascript
jQuery删除/清空指定元素的所有子节点实例代码
2019/07/04 jQuery
解决vue打包后刷新页面报错:Unexpected token
2019/08/27 Javascript
JS实现秒杀倒计时特效
2020/01/02 Javascript
[14:56]教你分分钟做大人:巫医
2014/10/30 DOTA
使用Python的Zato发送AMQP消息的教程
2015/04/16 Python
初步认识Python中的列表与位运算符
2015/10/12 Python
为什么选择python编程语言入门黑客攻防 给你几个理由!
2018/02/02 Python
python实现简易通讯录修改版
2018/03/13 Python
python获取服务器响应cookie的实例
2018/12/28 Python
对Xpath 获取子标签下所有文本的方法详解
2019/01/02 Python
python 通过手机号识别出对应的微信性别(实例代码)
2019/12/22 Python
使用Python内置模块与函数进行不同进制的数的转换
2020/04/26 Python
CSS3 :nth-child()伪类选择器实现奇偶行显示不同样式
2013/11/05 HTML / CSS
公司行政经理岗位职责
2013/12/24 职场文书
上班上网检讨书
2014/01/29 职场文书
环卫工作个人总结
2015/03/04 职场文书
2015年酒店工作总结
2015/04/28 职场文书
民政局未婚证明
2015/06/15 职场文书
KVM基础命令详解
2022/04/30 Servers