一、单继承
子类为了调用父类(超类)的一个方法,可以使用 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__')
二、多继承
涉及到多继承的代码中就有可能导致很奇怪的问题发生。 比如,考虑如下的情况:
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__
(, , ,
, )
>>>
为了实现继承,Python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。 我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
- 子类会先于父类被检查
- 多个父类会根据它们在列表中的顺序被检查
- 如果对下一个类存在两个合法的选择,选择第一个父类