我是 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()
。
根据 MRO __init__
,第三名被称为第一名。
接下来,根据 MRO,在__init__
方法super(Third, self).__init__()
解析为 First 的__init__
方法,该方法被调用。
在第一个super(First, self).__init__()
__init__
里面super(First, self).__init__()
调用第二个 __init__
,因为那是 MRO 的指示!
在第二个super(Second, self).__init__()
内部__init__
中super(Second, self).__init__()
调用对象的__init__
,这无关紧要。之后打印 “第二个” 。
在super(First, self).__init__()
完成后, 打印出 “first” 。
super(Third, self).__init__()
完成后, 打印出 “就是它” 。
这详细说明了为什么实例化 Third()会导致:
>>> x = Third()
second
first
that's it
MRO 算法已经从 Python 2.3 开始改进,在复杂的情况下运行良好,但我想在大多数情况下使用 “深度优先从左到右遍历”+“删除重复期望到最后” 仍然有效(请评论,如果不是这样)。一定要阅读 Guido 的博客文章!