协慌网

登录 贡献 社区

Python 的 super()如何与多重继承一起工作?

我是 Python 面向对象编程的新手,我很难理解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()运行哪一个?为什么?

如果你想运行另一个怎么办?我知道它与 Python 方法解析顺序( MRO )有关。

答案

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

你的代码和其他答案都是错误的。他们缺少合作子类化工作所需的前两个类中的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")

super()调用在每个步骤中找到 MRO 中的下一个方法,这就是为什么 First 和 Second 也必须拥有它,否则执行在Second.__init__()结束时停止。

这就是我得到的:

>>> Third()
second
first
third

我想通过一点点没有详细解释答案,因为当我开始阅读如何在 Python 中的多继承层次结构中使用 super()时,我没有立即得到它。

您需要了解的是super(MyClass, self).__init__()根据完整继承层次结构的上下文中使用的 Method Resolution Ordering(MRO)算法提供下一个 __init__方法。

最后一部分对于理解至关重要。让我们再考虑一下这个例子:

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 "that's it"

根据 Guido van Rossum 撰写的关于方法解决顺序文章 ,使用 “深度优先从左到右遍历” 计算解决__init__的顺序(在 Python 2.3 之前):

Third --> First --> object --> Second --> object

删除除最后一个之外的所有重复项后,我们得到:

Third --> First --> Second --> object

所以,让我们来看看当我们实例化Third类的实例时会发生什么,例如x = Third()

  1. 根据 MRO __init__ ,第三名被称为第一名。

  2. 接下来,根据 MRO,在__init__方法super(Third, self).__init__()解析为 First 的__init__方法,该方法被调用。

  3. 在第一个super(First, self).__init__() __init__里面super(First, self).__init__()调用第二个 __init__ ,因为那是 MRO 的指示!

  4. 在第二个super(Second, self).__init__()内部__init__super(Second, self).__init__()调用对象的__init__ ,这无关紧要。之后打印 “第二个”

  5. super(First, self).__init__()完成后, 打印出 “first”

  6. super(Third, self).__init__()完成后, 打印出 “就是它”

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

>>> x = Third()
second
first
that's it

MRO 算法已经从 Python 2.3 开始改进,在复杂的情况下运行良好,但我想在大多数情况下使用 “深度优先从左到右遍历”+“删除重复期望到最后” 仍然有效(请评论,如果不是这样)。一定要阅读 Guido 的博客文章!