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本人在他的博客文章Method Resolution Order(包括两次较早的尝试)中详细说明了这一点。

在你的例子中, Third()将调用First.__init__ 。 Python会在类的父项中查找从左到右列出的每个属性。 在这种情况下,我们正在寻找__init__ 。 所以,如果你定义

class Third(First, Second):
    ...

Python首先查看First ,如果First没有该属性,那么它将查看Second

当继承开始跨越路径时,这种情况变得更加复杂(例如,如果FirstSecond继承)。 阅读上面的链接了解更多细节,但是,简而言之,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是如何构建的。 他们打算打印“第一 nsecond 第三”或其他。 当然,你可以 - 也应该玩这个例子,添加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__()提供了下一个__init__方法,根据在完整继承层次结构中使用的方法解析顺序(MRO)算法。

这最后一部分是理解的关键。 我们再来看一下这个例子:

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的Method Resolution Order的文章,使用“深度优先从左到右遍历”计算了解决__init__的命令(在Python 2.3之前):

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

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

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

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

  • 根据MRO的__init__第一个被称为第一个。

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

  • 里面__init__首先super(First, self).__init__()调用__init__其次的,因为那是MRO决定的!

  • 里面__init__super(Second, self).__init__()调用__init__的对象,这相当于什么都没有。 之后, 打印“秒”

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

  • super(Third, self).__init__()完成后, 打印出“就是这样”

  • 这详细介绍了为什么将Third()结果实例化为:

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

    从Python 2.3开始,MRO算法已经得到了改进,在复杂情况下能够很好地工作,但我想大多数情况下,使用“深度优先从左到右遍历”+“去除重复期望的最后一个”仍然有效如果不是这种情况,请发表评论)。 请务必阅读Guido的博客文章!

    链接地址: http://www.djcxy.com/p/4531.html

    上一篇: How does Python's super() work with multiple inheritance?

    下一篇: Python's equivalent of && (logical