怎么理解python的super

super() 的基本使用

在类的继承中,如果重定义的某个方法,该方法会覆盖父类的同名方法,但同时我们希望同时实现父类的功能,这时,就需要调用父类的方法了,可以通过super来实现。比较常见的用法是在子类中调用父类的初始化方法了,比如:

class Base(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

class A(Base):
    def __init__(self, a, b, c):
        super(A, self).__init__(a, b)  # Python3 可使用 super().__init__(a, b)
        self.c = c

但是涉及到多重继承的时候,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"

c = C()

可以使用pycharm的调试工具调试在C这个类初始化的时候调试进去(step into),会发现最后执行的顺序是

enter C
enter A
enter B
enter Base
leave Base
leave B
leave A
leave C

正常的理解是super调用的是父类的方法,但是enter A之后并没有直接enter base,而是走到了enter B。这是什么原因呢?

这就涉及到了super在实现时的MRO(Method Resolution Order, MRO)机制 

MRO列表

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

>>> C.mro()   # or C.__mro__ or C().__class__.mro()
[__main__.C, __main__.A, __main__.B, __main__.Base, object]

一个类的 MRO 列表就是合并所有父类的 MRO 列表,并遵循以下三条原则:

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

现在,让我们回到前面的例子。

首先看类 C 的 __init__ 方法:

super(C, self).__init__()

这里的 self 是当前 C 的实例,self.__class__.mro() 结果是:

[__main__.C, __main__.A, __main__.B, __main__.Base, object]

可以看到,C 的下一个类是 A,于是,跳到了 A 的 __init__,这时会打印出 enter A,并执行下面一行代码:

super(A, self).__init__()

注意,这里的 self 也是当前 C 的实例,MRO 列表跟上面是一样的,搜索 A 在 MRO 中的下一个类,发现是 B,于是,跳到了 B 的 __init__,这时会打印出 enter B,而不是 enter Base。

enter B之后来到

super(B, self).__init__()

这时候self仍然是C的实例,MRO列表中的下一个类是Base,于是跳到Base类的init方法,打印出enter Base,leave Base. 然后是leave B, 这一部分代表super(A, self).__init__()执行结束,下一步则是leave A.到这里之后,代表super(C, self).__init__()执行结束,最后打印出leave C。程序结束。

小结:

  • 事实上,super 和父类没有实质性的关联。
  • super(cls, inst) 获得的是 cls 在 inst 的 MRO 列表中的下一个类。

猜你喜欢

转载自blog.csdn.net/qq_35462323/article/details/85135577