Python中的super()在多重继承中如何工作?

1336 投票
18 回答
620174 浏览
提问于 2025-04-16 01:29

在多重继承中,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 个回答

267

我想详细解释一下 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()

  1. 根据MRO,Third.__init__ 首先执行。
    • 打印 Third(): entering
    • 然后执行 super(Third, self).__init__(),MRO返回 First.__init__,这个方法被调用。
  2. First.__init__ 执行。
    • 打印 First(): entering
    • 然后执行 super(First, self).__init__(),MRO返回 Second.__init__,这个方法被调用。
  3. Second.__init__ 执行。
    • 打印 Second(): entering
    • 然后执行 super(Second, self).__init__(),MRO返回 object.__init__,这个方法被调用。
  4. object.__init__ 执行(这里没有打印语句)
  5. 执行回到 Second.__init__,然后打印 Second(): exiting
  6. 执行回到 First.__init__,然后打印 First(): exiting
  7. 执行回到 Third.__init__,然后打印 Third(): exiting

这详细说明了为什么实例化 Third() 会导致:

Third(): entering
First(): entering
Second(): entering
Second(): exiting
First(): exiting
Third(): exiting

从Python 2.3开始,MRO算法得到了改进,可以很好地处理复杂情况,但我想“深度优先从左到右遍历”加上“去掉重复项只保留最后一个”在大多数情况下仍然有效(如果不是,请评论告诉我)。一定要阅读Guido的博客文章!

386

你的代码和其他的回答都有问题。它们在前两个类里缺少了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)中的下一个方法,这就是为什么FirstSecond也必须有它的原因,否则执行会在Second.__init__()的最后停下来。


如果FirstSecond中没有super()的调用,输出结果就会缺少second

>>> Third()
first
third
917

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'>)

撰写回答