如果子类继承自两个单独的超类,而那两个超类又继承自同一个公共基类,那么就构成了钻石继承体系。这种继承体系很像竖立的菱形,也称作菱形继承。
class Base:
def __init__(self, value):
self.value = value
class One(Base):
def __init__(self, value):
Base.__init__(self, value)
self.value *= 2
class Two(Base):
def __init__(self, value):
Base.__init__(self, value)
self.value += 3
class Ways(One, Two):
def __init__(self, value):
One.__init__(self, value)
Two.__init__(self, value)
foo = Ways(5)
print(foo.value)
8
我们认为它会输出13,但结果却是8,因为在调用第二个超类时,即Two.__init__,它会再次调用Base.__init__,从而导致self.value重新变成5.
解决这个问题的方式是使用super方法,根据方法解析顺序(MRO)以标准化的流程来安排超类之间的初始化顺序,它也能够保证钻石顶部的公共基类的__init__方法之运行一次。
class Base:
def __init__(self, value):
self.value = value
class One(Base):
def __init__(self, value):
super(One, self).__init__(value)
self.value *= 2
class Two(Base):
def __init__(self, value):
super(Two, self).__init__(value)
self.value += 3
class Ways(One, Two):
def __init__(self, value):
super(Ways, self).__init__(value)
foo = Ways(5)
print(foo.value)
16
这是怎么回事?为何和我们设想的刚好相反。应该先运行One.__init__,然后运行Two.__init__。但实际却不一样,程序的运行会与Ways的MRO顺序保持一致。调用Ways(5)时,它会调用One.__init__,One.__init__又调用Two.__init__,Two.__init__在调用Base.__init__。到达了钻石继承的顶端,所有的初始化方法会按照与刚才那些__init__相反的顺序操作。
class Base:
def __init__(self, value):
self.value = value
class One(Base):
def __init__(self, value):
super(One, self).__init__(value * 2)
class Two(Base):
def __init__(self, value):
super(Two, self).__init__(value + 3)
class Ways(One, Two):
def __init__(self, value):
super(Ways, self).__init__(value)
foo = Ways(5)
print(foo.value)
13
这样做问题就解决了。