Python高级语法3:方法解析顺序表MRO

简介: Python高级语法3:方法解析顺序表MRO

一、多继承以及MRO顺序



  • 1.1、单独调用父类的方法: 父类名.__init__(self, 参数):不建议使用,父类的父类可能被调用多次


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")
print(Grandson.__mro__)
  • 执行的结果:


Grandson的init开始被调用
Son1的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son1的init结束被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Grandson的init结束被调用
姓名: grandson
年龄: 12
性别: 男
******多继承使用类名.__init__ 发生的状态******
(<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)
  • 1.2、多继承中super调用有所父类的被重写的方法:super().__init__(self, 参数)


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__ 发生的状态******


  • 注意:


  • 以上2个代码执行的结果不同
  • 如果2个子类中都继承了父类,当在子类中通过父类名调用时,parent被执行了2次
  • 如果2个子类中都继承了父类,当在子类中通过super调用时,parent被执行了1次
  • 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.4、总结
  • (1)、super().init相对于类名.init,在单继承上用法基本无差
  • (2)、但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次,具体看前面的输出结果
  • (3)、多继承时,使用super方法,对父类的传参数,应该是由于python中super的算法导致的原因,必须把参数全部传递,否则会报错
  • (4)、单继承时,使用super方法,则不能全部传递,只能传父类方法所需的参数,否则会报错
  • (5)、多继承时,相对于使用类名.init方法,要把每个父类全部写一遍, 而使用super方法,只需写一句话便执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因


  • 1.5、面试题:以下的代码的输出将是什么? 说出你的答案并解释。


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)
  • 答案, 以上代码的输出是:


1 1 1
 1 2 1
 3 2 3


  • 使你困惑或是惊奇的是关于最后一行的输出是 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。
目录
相关文章
|
8月前
|
XML JSON 数据处理
超越JSON:Python结构化数据处理模块全解析
本文深入解析Python中12个核心数据处理模块,涵盖csv、pandas、pickle、shelve、struct、configparser、xml、numpy、array、sqlite3和msgpack,覆盖表格处理、序列化、配置管理、科学计算等六大场景,结合真实案例与决策树,助你高效应对各类数据挑战。(238字)
1124 0
|
8月前
|
数据采集 存储 JavaScript
解析Python爬虫中的Cookies和Session管理
Cookies与Session是Python爬虫中实现状态保持的核心。Cookies由服务器发送、客户端存储,用于标识用户;Session则通过唯一ID在服务端记录会话信息。二者协同实现登录模拟与数据持久化。
|
9月前
|
人工智能 数据安全/隐私保护 异构计算
桌面版exe安装和Python命令行安装2种方法详细讲解图片去水印AI源码私有化部署Lama-Cleaner安装使用方法-优雅草卓伊凡
桌面版exe安装和Python命令行安装2种方法详细讲解图片去水印AI源码私有化部署Lama-Cleaner安装使用方法-优雅草卓伊凡
1466 8
桌面版exe安装和Python命令行安装2种方法详细讲解图片去水印AI源码私有化部署Lama-Cleaner安装使用方法-优雅草卓伊凡
|
9月前
|
JSON 缓存 开发者
淘宝商品详情接口(item_get)企业级全解析:参数配置、签名机制与 Python 代码实战
本文详解淘宝开放平台taobao.item_get接口对接全流程,涵盖参数配置、MD5签名生成、Python企业级代码实现及高频问题排查,提供可落地的实战方案,助你高效稳定获取商品数据。
|
9月前
|
存储 大数据 Unix
Python生成器 vs 迭代器:从内存到代码的深度解析
在Python中,处理大数据或无限序列时,迭代器与生成器可避免内存溢出。迭代器通过`__iter__`和`__next__`手动实现,控制灵活;生成器用`yield`自动实现,代码简洁、内存高效。生成器适合大文件读取、惰性计算等场景,是性能优化的关键工具。
432 2
|
9月前
|
测试技术 开发者 Python
Python单元测试入门:3个核心断言方法,帮你快速定位代码bug
本文介绍Python单元测试基础,详解`unittest`框架中的三大核心断言方法:`assertEqual`验证值相等,`assertTrue`和`assertFalse`判断条件真假。通过实例演示其用法,帮助开发者自动化检测代码逻辑,提升测试效率与可靠性。
595 1
|
9月前
|
算法 调度 决策智能
【两阶段鲁棒优化】利用列-约束生成方法求解两阶段鲁棒优化问题(Python代码实现)
【两阶段鲁棒优化】利用列-约束生成方法求解两阶段鲁棒优化问题(Python代码实现)
282 0
|
9月前
|
机器学习/深度学习 文字识别 Java
Python实现PDF图片OCR识别:从原理到实战的全流程解析
本文详解2025年Python实现扫描PDF文本提取的四大OCR方案(Tesseract、EasyOCR、PaddleOCR、OCRmyPDF),涵盖环境配置、图像预处理、核心识别与性能优化,结合财务票据、古籍数字化等实战场景,助力高效构建自动化文档处理系统。
2405 0
|
9月前
|
机器学习/深度学习 JSON Java
Java调用Python的5种实用方案:从简单到进阶的全场景解析
在机器学习与大数据融合背景下,Java与Python协同开发成为企业常见需求。本文通过真实案例解析5种主流调用方案,涵盖脚本调用到微服务架构,助力开发者根据业务场景选择最优方案,提升开发效率与系统性能。
2060 0
机器学习/深度学习 算法 自动驾驶
1430 0

推荐镜像

更多