Python中的super()在多重继承中如何工作?
在多重继承中,super()
是怎么工作的呢?举个例子:
class First(object):
def __init__(self):
print "first"
class Second(object):
def __init__(self):
print "second"
class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print "that's it"
那么在Third
这个类中,super().__init__
指的是哪个父类的方法呢?我可以选择哪个方法先执行吗?
我知道这和方法解析顺序(MRO)有关系。
18 个回答
我想详细解释一下 lifeless的回答,因为当我开始学习如何在Python的多重继承中使用super()时,一开始并没有完全明白。
你需要明白的是,super(MyClass, self).__init__()
是根据使用的方法解析顺序(MRO)算法,在整个继承层次结构中提供下一个__init__
方法。
这一点非常重要。我们再来看一个例子:
#!/usr/bin/env python2
class First(object):
def __init__(self):
print "First(): entering"
super(First, self).__init__()
print "First(): exiting"
class Second(object):
def __init__(self):
print "Second(): entering"
super(Second, self).__init__()
print "Second(): exiting"
class Third(First, Second):
def __init__(self):
print "Third(): entering"
super(Third, self).__init__()
print "Third(): exiting"
根据Guido van Rossum写的关于方法解析顺序的文章,解决__init__
的顺序是通过“深度优先从左到右遍历”来计算的(在Python 2.3之前):
Third --> First --> object --> Second --> object
在去掉所有重复项后,除了最后一个,我们得到:
Third --> First --> Second --> object
那么,让我们看看当我们创建Third
类的实例时会发生什么,比如说x = Third()
。
- 根据MRO,
Third.__init__
首先执行。- 打印
Third(): entering
- 然后执行
super(Third, self).__init__()
,MRO返回First.__init__
,这个方法被调用。
- 打印
First.__init__
执行。- 打印
First(): entering
- 然后执行
super(First, self).__init__()
,MRO返回Second.__init__
,这个方法被调用。
- 打印
Second.__init__
执行。- 打印
Second(): entering
- 然后执行
super(Second, self).__init__()
,MRO返回object.__init__
,这个方法被调用。
- 打印
object.__init__
执行(这里没有打印语句)- 执行回到
Second.__init__
,然后打印Second(): exiting
- 执行回到
First.__init__
,然后打印First(): exiting
- 执行回到
Third.__init__
,然后打印Third(): exiting
这详细说明了为什么实例化 Third()
会导致:
Third(): entering
First(): entering
Second(): entering
Second(): exiting
First(): exiting
Third(): exiting
从Python 2.3开始,MRO算法得到了改进,可以很好地处理复杂情况,但我想“深度优先从左到右遍历”加上“去掉重复项只保留最后一个”在大多数情况下仍然有效(如果不是,请评论告诉我)。一定要阅读Guido的博客文章!
你的代码和其他的回答都有问题。它们在前两个类里缺少了super()
的调用,这个调用是为了让子类之间能够合作工作。更好的写法是:
class First(object):
def __init__(self):
super(First, self).__init__()
print("first")
class Second(object):
def __init__(self):
super(Second, self).__init__()
print("second")
class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print("third")
输出结果:
>>> Third()
second
first
third
super()
这个调用在每一步都会找到方法解析顺序(MRO)中的下一个方法,这就是为什么First
和Second
也必须有它的原因,否则执行会在Second.__init__()
的最后停下来。
如果在First
和Second
中没有super()
的调用,输出结果就会缺少second
:
>>> Third()
first
third
Guido在他的博客文章方法解析顺序中详细讲解了这个问题,包括他之前的两个尝试。
在你的例子中,Third()
会调用First.__init__
。Python会从左到右查找每个类的父类中的属性。在这个情况下,我们要找的是__init__
。所以,如果你定义了
class Third(First, Second):
...
Python会先查看First
,如果First
没有这个属性,它就会去看Second
。
当继承关系变得复杂时,情况就会更麻烦(比如如果First
是从Second
继承的)。想了解更多细节可以看上面的链接,但简单来说,Python会尽量保持每个类在继承列表中出现的顺序,从子类开始。
举个例子,如果你有:
class First(object):
def __init__(self):
print "first"
class Second(First):
def __init__(self):
print "second"
class Third(First):
def __init__(self):
print "third"
class Fourth(Second, Third):
def __init__(self):
super(Fourth, self).__init__()
print "that's it"
那么MRO(方法解析顺序)会是[Fourth, Second, Third, First].
顺便提一下:如果Python找不到一个合理的方法解析顺序,它会抛出一个异常,而不是做一些让用户感到意外的事情。
模糊的MRO示例:
class First(object):
def __init__(self):
print "first"
class Second(First):
def __init__(self):
print "second"
class Third(First, Second):
def __init__(self):
print "third"
那么Third
的MRO应该是[First, Second]
还是[Second, First]
呢?没有明显的预期,Python会抛出一个错误:
TypeError: Error when calling the metaclass bases
Cannot create a consistent method resolution order (MRO) for bases Second, First
为什么上面的例子没有super()
的调用?这些例子的目的是展示MRO是如何构建的。它们并不是为了打印"first\nsecond\third"
或其他什么。你当然可以,也应该尝试这个例子,添加super()
的调用,看看会发生什么,从而更深入地理解Python的继承模型。但我在这里的目标是保持简单,展示MRO是如何构建的。它的构建方式正如我所解释的那样:
>>> Fourth.__mro__
(<class '__main__.Fourth'>,
<class '__main__.Second'>, <class '__main__.Third'>,
<class '__main__.First'>,
<type 'object'>)