Python中的"super"如何做到正确的事情?
Python中的"super"如何做到正确的事情?
我正在使用 Python 2.5,因此这个问题可能不适用于 Python 3。当您使用多重继承创建一个菱形类层次结构并创建派生最多的类的对象时,Python 会做正确的事情(TM)。它按从左到右列出的父类的顺序调用派生的构造函数,然后是父类,最后是祖先。我熟悉 Python 的MRO,这不是我的问题。我想知道从 super 返回的实际对象如何成功地与父类中的 super 调用通信以获取正确的顺序。考虑下面的示例代码:
#!/usr/bin/python class A(object): def __init__(self): print "A init" class B(A): def __init__(self): print "B init" super(B, self).__init__() class C(A): def __init__(self): print "C init" super(C, self).__init__() class D(B, C): def __init__(self): print "D init" super(D, self).__init__() x = D()
代码做出了直观的事情,它打印出:
D init B init C init A init
然而,如果你注释掉 B 的 init 函数中的 super 调用,则不会调用 A 或 C 的 init 函数。这意味着 B 的 super 调用以某种方式意识到 C 在整个类层次结构中的存在。我知道 super 返回一个代理对象,它具有重载的 get 操作符,但是在 D 的 init 定义中返回的对象如何通知在 B 的 init 定义中返回的对象以存在 C?后续的 super 调用使用的信息是否存储在对象本身上?如果是这样,为什么不是 self.super 而是 super ?
编辑:Jekke 正确指出 super 不是 self.super,因为 super 是类的属性,而不是类的实例。这在概念上是有道理的,但实际上 super 也不是类的属性!您可以在解释器中制作两个类 A 和 B,其中 B 继承自 A,并调用 dir(B)
。它没有 super 或 __super__ 属性。
我在下面提供了一堆链接,它们比我所能做到的更详细,更精确地回答了你的问题。然而我也会用我的自己的话回答你的问题,以便为你节约时间。以下是我的回答:
super
是一个内置函数,而不是一个属性。- Python中的每个类型(类)都有一个
__mro__
属性,它存储了该特定实例的方法调用顺序。 - 每次
super
的调用形式都是super(type[, object-or-type])
。现在我们假设第二个属性暂时是一个对象。 - 在
super
调用的起点,对象是派生类的类型(假设为DC)。 super
在MRO中查找匹配(在您的情况下是__init__
)的方法,这些方法在第一个参数指定的类之后的类中找到(在这种情况下是DC之后的类)。- 当找到匹配方法时(假设在
BC1
类中),它被调用。
(这个方法应该使用super
,所以我假设这样做了 - 请参见下面的Python的super
非常棒,但不能使用 - 链接) - 然后,该方法会导致在对象的类的MRO中搜索下一个方法,位于
BC1
右侧。 - 重复上述过程直到找到并调用所有方法。
针对您的示例的解释
MRO: D,B,C,A,object
super(D, self).__init__()
被调用。isinstance(self, D) => True-
在MRO中搜索在D右侧的类中的下一个方法。
找到并调用
B.__init__
-
B.__init__
调用super(B, self).__init__()
。isinstance(self, B) => False
isinstance(self, D) => True -
因此,MRO相同,但搜索继续到B的右边,即逐一搜索C、A、object。找到下一个
__init__
并调用它。 -
以此类推。
super的解释
http://www.python.org/download/releases/2.2.3/descrintro/#cooperation
使用super时要注意的事项
http://fuhm.net/super-harmful/
Python的MRO算法:
http://www.python.org/download/releases/2.3/mro/
super的文档:
http://docs.python.org/library/functions.html
本页底部有一个有关super的不错的部分:
http://docstore.mik.ua/orelly/other/python/0596001886_pythonian-chp-5-sect-2.html
希望这有所帮助,澄清了问题。
将你的代码更改为以下内容,我认为它可以解释一些事情(很可能是super
正在查看__mro__
中B
所处的位置 ?):
class A(object): def __init__(self): print "A init" print self.__class__.__mro__ class B(A): def __init__(self): print "B init" print self.__class__.__mro__ super(B, self).__init__() class C(A): def __init__(self): print "C init" print self.__class__.__mro__ super(C, self).__init__() class D(B, C): def __init__(self): print "D init" print self.__class__.__mro__ super(D, self).__init__() x = D()
如果你运行它,你会看到:
D init (, , , , ) B init ( , , , , ) C init ( , , , , ) A init ( , , , , )
另外,值得一提的是,要查看Python的超级(Super)很棒,但你不能使用它。