子类为了调用父类(超类)的一个方法,可以使用 super()
函数,比如:
class A: def spam(self): print('A.spam') class B(A): def spam(self): print('B.spam') super().spam() # Call parent spam()
super()
函数的一个常见用法是在 __init__()
方法中确保父类被正确的初始化了:
class A: def __init__(self): self.x = 0 class B(A): def __init__(self): super().__init__() self.y = 1
也可以这样直接调用父类的一个方法:
class Base: def __init__(self): print('Base.__init__') class A(Base): def __init__(self): Base.__init__(self) print('A.__init__')
1.2. 多继承
涉及到多继承的代码中就有可能导致很奇怪的问题发生。 比如,考虑如下的情况:
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__')
如果你运行这段代码就会发现 Base.__init__()
被调用两次,如下所示:
>>> c = C() 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__()
方法只会被调用一次了:
>>> c = C() Base.__init__ B.__init__ A.__init__ C.__init__ >>>
为了弄清它的原理,我们需要花点时间解释下Python是如何实现继承的。 对于你定义的每一个类,Python会计算出一个所谓的方法解析顺序(MRO)列表。 这个MRO列表就是一个简单的所有基类的线性顺序表。例如:
>>> C.__mro__ (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>) >>>
为了实现继承,Python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。 我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
- 子类会先于父类被检查
- 多个父类会根据它们在列表中的顺序被检查
- 如果对下一个类存在两个合法的选择,选择第一个父类
你所要知道的就是MRO列表中的类顺序会让你定义的任意类层级关系变得有意义。
当你使用 super()
函数时,Python会在MRO列表上继续搜索下一个类。 只要每个重定义的方法统一使用 super()
并只调用它一次, 那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次。 这也是为什么在第二个例子中你不会调用两次 Base.__init__()
的原因。
super()
有个令人吃惊的地方是它并不一定去查找某个类在MRO中下一个直接父类, 你甚至可以在一个没有直接父类的类中使用它。例如,考虑如下这个类:
class A: def spam(self): print('A.spam') super().spam()
如果你试着直接使用这个类就会出错:
>>> a = A() >>> a.spam() A.spam Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in spam AttributeError: 'super' object has no attribute 'spam' >>>
但是,如果你使用多继承的话看看会发生什么:
>>> class B: ... def spam(self): ... print('B.spam') ... >>> class C(A,B): ... pass ... >>> c = C() >>> c.spam() A.spam B.spam >>>