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 列表中的下一个类。