python中的继承机制super()函数详解

 更新时间:2023年08月24日 14:14:43   作者:goodxin_ie  
这篇文章主要介绍了python中的继承机制super()函数详解,super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序、重复调用等问题,需要的朋友可以参考下

前言

super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题

但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。

一、super用法

我们先简单的理解为super().xx相当于调用了父类中的xx方法(实际上在单继承中是这样,多继承中有点区别)。

时候会看到像下面这样直接调用父类的一个方法:

class Base:
    def __init__(self):
        print('Base.__init__')
class A(Base):
    def __init__(self):
        Base.__init__(self)
        print('A.__init__')

尽管对于大部分代码而言这么做没什么问题,但是在更复杂的涉及到多继承的代码中就有可能导致很奇怪的问题发生。

比如,考虑如下的情况:

class Base:
    def __init__(self):
        print('Base.__init__')
class A(Base):
    def __init__(self):
        Base.__init__(self)
        print('A.__init__')
class B(Base):
    def __init__(self):
        Base.__init__(self)
        print('B.__init__')
class C(A,B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)
        print('C.__init__')
c = C()

如果你运行这段代码就会发现 Base.__init__() 被调用两次,如下所示:

 
Base.__init__
A.__init__
Base.__init__
B.__init__
C.__init__

但是当我们在代码中换成使用 super() ,结果就很完美了:

class Base:
    def __init__(self):
        print('Base.__init__')
class A(Base):
    def __init__(self):
        super().__init__()
        print('A.__init__')
class B(Base):
    def __init__(self):
        super().__init__()
        print('B.__init__')
class C(A,B):
    def __init__(self):
        super().__init__()  # Only one call to super() here
        print('C.__init__')

运行这个新版本后,你会发现每个 __init__() 方法只会被调用一次了:

Base.__init__
B.__init__
A.__init__
C.__init__

二、super的本质

先说说python中如何实现继承---------对于你定义的每一个类,Python会计算出一个所谓的方法解析顺序(MRO)列表。 这个MRO列表就是一个简单的所有基类的线性顺序表。为了实现继承,Python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。

而这个MRO列表的构造是通过一个C3线性化算法来实现的。 我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:

  • 子类会先于父类被检查
  • 多个父类会根据它们在列表中的顺序被检查
  • 如果对下一个类存在两个合法的选择,选择第一个父类

虽然名义上来说super是用来调用父类中的方法,但是super实际上是在MRO表中找到下一个匹配的类。super原型如下:

def super(cls, inst):
  mro = inst.__class__.mro()
  return mro[mro.index(cls) + 1]

两个参数 cls 和 inst 分别做了两件事:

1. inst 负责生成 MRO 的 list

2. 通过 cls 定位当前 MRO 中的 index, 并返回 mro[index + 1]

我们来看一个例子,猜猜下面的输出会是什么呢:

class A():
    def __init__(self):         
        print("Enter A")  
class B(A):
    def __init__(self):  
        print("Enter B")         
        super(B,self).__init__()  
        print("Leave B")       
class C(A):
    def __init__(self): 
        print("Enter C")            
        super(C,self).__init__()  
        print("Leave C")      
class D(B,C):
    def __init__(self): 
        print("Enter D")            
        super(D,self).__init__()
        print("Leave D")   
d = D()

直接看结果:

很多人将super简单的理解为调用父类中的方法,可能认为应该是D调用B和C,由于B在左边,按顺序先调用B,B油调用A,完成之后轮到D调用C,C调用A.输出变成下面这样:

Enter D
Enter B
Enter A
Leave B
Enter C
Enter A
Leave C
Leave D

但是根据我们上面说的super本质知道super 和父类其实没有实质关联,我们就不难理解为什么 enter B 下一句是 enter C 而不是 enter A了(如果认为 super 代表“调用父类的方法”,会想当然的认为下一句应该是enter A)。流程如下,在 B 的 __init__ 函数中:

super(B,self).__init__() 首先获取self.__class__.__mro__,但是这里的self是D的实例,而不是B的。

(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

然后,通过 B 来定位 MRO 中的 index,并找到下一个。

显然 B 的下一个是 C。于是,我们调用 C 的 __init__,打出 enter C。

当你使用super()函数时,Python会在MRO列表上继续搜索下一个类。

只要每个重定义的方法统一使用super()并只调用它一次, 那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次。

到此这篇关于python中的继承机制super()函数详解的文章就介绍到这了,更多相关python继承super()函数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Python之Numpy的超实用基础详细教程

    Python之Numpy的超实用基础详细教程

    这篇文章主要介绍了Python之Numpy的超实用基础详细教程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-10-10
  • PyChar学习教程之自定义文件与代码模板详解

    PyChar学习教程之自定义文件与代码模板详解

    pycharm默认的【新建】文件,格式很不友好,那么就需要改一下文件模板。下面这篇文章主要给大家介绍了关于PyChar学习教程之自定义文件与代码模板的相关资料,文中通过示例代码介绍的非常详细,需要的朋友们下面跟着小编来一起看看吧。
    2017-07-07
  • django filter过滤器实现显示某个类型指定字段不同值方式

    django filter过滤器实现显示某个类型指定字段不同值方式

    这篇文章主要介绍了django filter过滤器实现显示某个类型指定字段不同值方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-07-07
  • Python绘制柱状图堆叠图完整代码

    Python绘制柱状图堆叠图完整代码

    有个朋友要求帮忙绘制堆叠柱状图,查阅了一些文档之后也算是完成了,只是一个小demo,下面我就记录一下,这篇文章主要给大家介绍了关于Python绘制柱状图堆叠图的相关资料,需要的朋友可以参考下
    2023-09-09
  • Python使用Spire.XLS进行插入,修改和删除迷你图

    Python使用Spire.XLS进行插入,修改和删除迷你图

    这篇文章主要为大家详细介绍如何使用 Spire.XLS for Python 在 Excel 中插入、修改和删除迷你图,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下
    2025-08-08
  • Python使用cProfile进行性能分析

    Python使用cProfile进行性能分析

    cProfile是Python标准库中的一个模块,用于收集代码的性能数据,这篇文章主要为大家详细介绍了如何使用cProfile进行性能分析,需要的可以参考下
    2024-12-12
  • 手把手教你打造个性化全栈应用Python Reflex框架全面攻略

    手把手教你打造个性化全栈应用Python Reflex框架全面攻略

    Reflex框架是为了解决传统全栈开发中的一些挑战而诞生的,它充分利用了现代前端框架(如React)的优势,与后端技术(如Node.js)深度集成,使得开发者能够更加流畅地构建整个应用,Reflex的设计理念包括简化、响应性和一致性,旨在提高全栈开发的效率和可维护性
    2023-12-12
  • 用60行代码实现Python自动抢微信红包

    用60行代码实现Python自动抢微信红包

    这篇文章主要介绍了用60行代码实现Python自动抢微信红包,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-02-02
  • 分享Python切分字符串的一个不错方法

    分享Python切分字符串的一个不错方法

    今天小编就为大家分享一篇Python切分字符串的一个不错方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-12-12
  • Python functools模块学习总结

    Python functools模块学习总结

    这篇文章主要介绍了Python functools模块学习总结,本文讲解了functools.partial、functool.update_wrapper、functool.wraps、functools.reduce、functools.cmp_to_key、functools.total_ordering等方法的使用实例,需要的朋友可以参考下
    2015-05-05

最新评论