欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

深入理解super

程序员文章站 2024-03-24 13:47:28
...

在前段时间使用类的继承时,为了在子类构造函数中调用父类的构造函数,我们使用到了super方法。

使用套路如下:
深入理解super

super不仅仅只用在构造函数中调用父类的构造函数,普通的方法中也可以使用。

深入理解super()

看了上面的使用,我们可能会觉得super的使用很简单,无非就是获取父类,并调用父类的方法。其实,在上面的情况中,super获取到的正好是父类,但在其他情况下就不一定了。super其实和父类没有实质性的联系。

让我们写一个稍微复杂的例子,涉及到多重继承。

class Base(object):
    def __init__(self):
        print "enter Base"
        print "leave Base"

class A(Base):
    def __init__(self):
        print "enter A"
        super(A, self).__init__()
        print "leave A"

class B(Base):
    def __init__(self):
        print "enter B"
        super(B, self).__init__()
        print "leave B"

class C(A, B):
    def __init__(self):
        print "enter C"
        super(C, self).__init__()
        print "leave C"

其中,Base是父类,A、B都继承自Base,C继承自A、B,它们的关系是一个典型的菱形继承,如下:

      Base
      /  \
     /    \
    A      B
     \    /
      \  /
       C

使用情况如下:
深入理解super

如果你认为super代表【调用父类的方法】,那么如何解释enterA的下一句不是enter Base而是enter B。原因是,super和父类没有实质性的关联。

MRO列表

事实上,对于你定义的每一个类,Python会计算出一个方法解析顺序(Method Resolution Order, MRO)列表。它代表了类继承的顺序。我们可以使用下面方法获取一个类的MRO列表。

类名.mro()

深入理解super

MRO列表的顺序是怎么定的呢?总的来说,一个类的MRO列表就是合并所有父类的MRO列表,并遵循以下三条原则:

  • 子类永远在父类的前面
  • 如果有多个父类,会根据它们在列表中的顺序依次被检查。
  • 如果对下一个类存在两个合法的选择,选择第一个父类(并没有理解说的是什么)

super原理

super的工作原理如下:

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

其中,cls代表类,inst代表的是实例,上面的代码做了两件事:

  • 获取inst的MRO列表
  • 查找cls在当前MRO列表中的index,并返回它的下一个类,即mro[index + 1]

所以说,在当前类中调用super返回的是MRO列表中的下一个类,并不一定是父类,也可能是同级类(多重继承的情况)
当你使用super(cls, inst)时,Python会在inst的MRO列表上搜索cls的下一个类。

至此,我们就解决了之前先输出enter B后输出enter Base的问题。
只要理解了super的工作机制,我们才会对这样的输出感到疑惑。

小结

  • 事实上,super和父类没有实质性关联。
  • super(cls, inst)获得的是当前cls(类)在MRO列表中的下一个类。
相关标签: 深入理解super