• Python内置函数super()的作用详解【用最简单的例子把函数super()的作用和运行过程说清楚)】


    函数super()用于子类调用父类(超类)的方法。

    你肯定要问为什么在子类中在要用函数super()去调用父类中的方法呢?

    我像下面这样用不行么?

    class A:
        def fun1(self):
            print('Enter A')
    
    
    class B(A):
        def fun1(self):
            A.fun1(self)
            print('Enter B')
    
    
    object1 = B()
    object1.fun1()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    运行结果如下:
    在这里插入图片描述

    是,的确是可以像上面这样,但是这样做的缺点是,当一个子类的父类发生变化时(如类B的父类由A变为C时),必须遍历整个B类的定义,把所有的相关调用名全修改过来。
    上面是例子很简单,只有一处需要修改,上面的缺点并不足以成为问题,但是当代码量庞大时,这样的修改可能是灾难性的。
    Python中向我们提供了内置函数super()来克服这个缺点和问题,来看一个使用内置函数super()来调用父类方法的示例。

    class A:
        def fun1(self):
            print('Enter A')
    
    
    class B(A):
        def fun1(self):
            super(B, self).fun1()
            print('Enter B')
    
    
    object1 = B()
    object1.fun1()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    运行结果如下:
    在这里插入图片描述
    通过上面的代码,我们可以看出,如果类B的父类A改为了C,则只需要把类定义中的第一句语句:

    class B(A):
    
    • 1

    改为

    class B(C):
    
    • 1

    就行了,而不用更改相关的调用语句。

    在Python3中,语句

    super(B, self).fun1()
    
    • 1

    可以简写为:

    super().fun1()
    
    • 1

    从上面来看,函数super()虽然把父类的名字隐去了,使得调用父类的方法与父类的名字无关,但是也带来了问题。什么问题呢?看下面的例子:

    class A:
        def fun1(self):
            print('Enter A')
    
    
    class B(A):
        def fun1(self):
            A.fun1(self)
            print('Enter B')
    
    
    class C(A):
        def fun1(self):
            A.fun1(self)
            print('Enter C')
    
    
    class D(B, C):
        def fun1(self):
            B.fun1(self)
            print('Enter D')
    
    
    object1 = D()
    object1.fun1()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    通过上面的代码,我们可以很清晰地看出子类D有两个父类B和C,即D是一种多重继承。在D中调用的方法fun1()是父类B中的方法fun1(),所以运行结果如下:
    在这里插入图片描述
    但我们若用函数super隐去父类名,这就出问题了,代码如下:

    class A:
        def fun1(self):
            print('Enter A')
    
    
    class B(A):
        def fun1(self):
            super().fun1()
            print('Enter B')
    
    
    class C(A):
        def fun1(self):
            super().fun1()
            print('Enter C')
    
    
    class D(B, C):
        def fun1(self):
            super().fun1()
            print('Enter D')
    
    
    object1 = D()
    object1.fun1()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    上面的代码存在的问题就是类D对父类中fun1的调用不知道该去调用哪个父类中的fun1。函数super()是这样处理这个问题的:采用MRO顺序把所有解析到的父类fun1都运行一遍且每个找到的方法fun1只运行一次。
    我们可以用下面这条语句把类D的MRO顺序打印出来:

    print(D.__mro__)
    
    • 1

    示例代码如下:

    class A:
        def fun1(self):
            print('Enter A')
    
    
    class B(A):
        def fun1(self):
            super().fun1()
            print('Enter B')
    
    
    class C(A):
        def fun1(self):
            super().fun1()
            print('Enter C')
    
    
    class D(B, C):
        def fun1(self):
            super().fun1()
            print('Enter D')
    
    
    print(D.__mro__)  # 把类D的MRO顺序打印出来
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    运行结果如下:
    在这里插入图片描述

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

    根据上面的MRO顺序,我们可以知道类D查询某方法的顺序为类D→类B→类C→类A。
    根据以上顺序,我们有以下关于object1.fun1()这条语句的执行过程:
    object1.fun1()的第一条需要执行的语句为类D中的语句super().fun1(),这条语句在执行后:
    首先在类D中找寻方法fun1(),找到了,找到之后将类D中的方法fun1()编号为①
    然后在类B中找寻方法fun1(),找到了,找到之后将类B中的方法fun1()编号为②
    然后在类C中找寻方法fun1(),找到了,找到之后将类C中的方法fun1()编号为③
    然后在类A中找寻方法fun1(),找到了,找到之后将类A中的方法fun1()编号为④
    然后按照④→③→②→①的顺序依次执行一遍一个四个fun1()
    当执行④时,函数体唯一的一条语句“print(‘Enter A’)”打印出字符串Enter A
    ④执行完后执行③,当执行③时,第一条语句为super().fun1(),于是按下面的顺序去搜索fun1:
    在这里插入图片描述
    首先在类C中找寻方法fun1(),找到了,找到之后发现类C中的方法fun1()已经被编号为③
    然后在类A中找寻方法fun1(),找到了,找到之后发现类A中的方法fun1()已经被编号为④
    然后按照④→③的顺序依次执行一遍一个两个fun1(),正准备执行④的时候,发现④已经被执行过一次了,所以不再执行④,于是直接执行③,执行③的时候发现其第一条语句super().fun1()已经被执行过一次了,所以不再执行这条语句,于是执行语句print(‘Enter C’),打印出字符串Enter C,至此③执行完毕。
    ③执行完后执行②,当执行②时,第一条语句为super().fun1(),于是按下面的顺序去搜索fun1:
    在这里插入图片描述
    首先在类B中找寻方法fun1(),找到了,找到之后发现类B中的方法fun1()已经被编号为②
    然后在类A中找寻方法fun1(),找到了,找到之后发现类A中的方法fun1()已经被编号为④
    然后按照④→②的顺序依次执行一遍一个两个fun1(),正准备执行④的时候,发现④已经被执行过一次了,所以不再执行④,于是直接执行②,执行②的时候发现其第一条语句super().fun1()已经被执行过一次了,所以不再执行这条语句,于是执行语句print(‘Enter B’),打印出字符串Enter B。
    ②执行完后执行①,当执行①时,第一条语句为super().fun1(),这条语句已经被执行过了,所以不再执行这条语句,于是执行剩下的一条语句print(‘Enter D’),打印出字符串Enter D。
    综上,输出信息应该如下:
    Enter A
    Enter C
    Enter B
    Enter D
    我们看下程序的运行结果是不是这样的:
    在这里插入图片描述
    从上面的截图来看,程序实际运行的结果和我们的分析结果是一致的,所以我们的分析是正确的。

  • 相关阅读:
    哪个才是最适合你的 Web UI 自动化测试框架
    (七)CSharp-CSharp图解教程版-事件
    11.14-11.21
    直播预告丨中高频多因子库存储的最佳实践
    【CSS颜色指南】:看完这一篇就明白CSS常见颜色表示方法
    centos7 fastdfs 5.0.5 离线安装
    jmeter-进阶02
    C语言详解系列——函数的认识(3)形参,实参,嵌套调用和链式访问
    牛客前端宝典——刷题 ##Day8
    栈与队列--删除字符串中的所有相邻重复项
  • 原文地址:https://blog.csdn.net/wenhao_ir/article/details/125472478