Python的“超级”是如何实现的? 做正确的事吗?
我正在运行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 定义中 super 返回的对象如何将 C 的存在传达给 B 的 init 定义中 super 返回的对象? 后续调用 super 使用的信息是否存储在对象本身上? 如果是这样,为什么不是 super 而不是 self.super?
编辑:Jekke 非常正确地指出它不是 self.super 因为 super 是类的属性,而不是类的实例。 从概念上讲这是有道理的,但实际上 super 也不是该类的属性! 您可以在解释器中通过创建两个类 A 和 B(其中 B 继承自 A)并调用 dir(B) 来测试这一点。 它没有 super
或 __super__
属性。
I'm running Python 2.5, so this question may not apply to Python 3. When you make a diamond class hierarchy using multiple inheritance and create an object of the derived-most class, Python does the Right Thing (TM). It calls the constructor for the derived-most class, then its parent classes as listed from left to right, then the grandparent. I'm familiar with Python's MRO; that's not my question. I'm curious how the object returned from super actually manages to communicate to calls of super in the parent classes the correct order. Consider this example code:
#!/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()
The code does the intuitive thing, it prints:
D init
B init
C init
A init
However, if you comment out the call to super in B's init function, neither A nor C's init function is called. This means B's call to super is somehow aware of C's existence in the overall class hierarchy. I know that super returns a proxy object with an overloaded get operator, but how does the object returned by super in D's init definition communicate the existence of C to the object returned by super in B's init definition? Is the information that subsequent calls of super use stored on the object itself? If so, why isn't super instead self.super?
Edit: Jekke quite rightly pointed out that it's not self.super because super is an attribute of the class, not an instance of the class. Conceptually this makes sense, but in practice super isn't an attribute of the class either! You can test this in the interpreter by making two classes A and B, where B inherits from A, and calling dir(B)
. It has no super
or __super__
attributes.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
将您的代码更改为此,我认为它会解释一些事情(大概
super
正在查看B
位于__mro__
中的位置? ):如果你运行它,你会看到:
另外,值得一试 Python 的 Super 很漂亮,但你不能使用它。
Change your code to this and I think it'll explain things (presumably
super
is looking at where, say,B
is in the__mro__
?):If you run it you'll see:
Also it's worth checking out Python's Super is nifty, but you can't use it.
我在下面提供了一些链接,它们比我希望的更详细、更准确地回答了您的问题。 不过,我也会用我自己的话回答你的问题,以节省你的时间。 我将把它放在点上 -
__mro__
属性,用于存储该特定实例的方法解析顺序。__init__
)。(这个方法应该使用 super,所以我假设它确实如此 - 请参阅Python's super is nifty but can't be use - 下面的链接)
然后,该方法会在对象类的 MRO 中搜索 BC1 右侧的下一个方法。
示例说明
super(D, self).__init__()
被调用。 isinstance(self, D) =>; True在 D 右侧的类中的 MRO 中搜索下一个方法。
B.__init__
找到并调用B.__init__
调用super(B, self).__init__()
。isinstance(self, B) =>; 错误
isinstance(self, D) =>; True
因此,MRO是相同的,但是搜索继续到B的右侧,即C,A,对象被一一搜索。 调用下一个找到的
__init__
。等等。
super的解释
http://www.python.org/download/releases/2.2 .3/descrintro/#合作
使用super时需要注意的事项
http://fuhm.net/super-harmful/
Python MRO 算法:
http://www.python.org/download/releases/2.3/mro/< /a>
super 的文档:
http://docs.python.org/library/functions.html
此页面的底部有一个关于超级的很好的部分:
http://docstore.mik.ua/ orelly/other/python/0596001886_pythonian-chp-5-sect-2.html
我希望这有助于澄清它。
I have provided a bunch of links below, that answer your question in more detail and more precisely than I can ever hope to. I will however give an answer to your question in my own words as well, to save you some time. I'll put it in points -
__mro__
attribute, that stores the method resolution order of that particular instance.__init__
) in the classes in the MRO, after the class specified as the first argument (in this case classes after DC).(This method should use super, so I am assuming it does - See Python's super is nifty but can't be used - link below)
That method then causes a search in the object's class' MRO for the next method, to the right of BC1.
Explanation for your example
super(D, self).__init__()
is called. isinstance(self, D) => TrueSearch for next method in the MRO in classes to the right of D.
B.__init__
found and calledB.__init__
callssuper(B, self).__init__()
.isinstance(self, B) => False
isinstance(self, D) => True
Thus, the MRO is the same, but the search continues to the right of B i.e. C,A,object are searched one by one. The next
__init__
found is called.And so on and so forth.
An explanation of super
http://www.python.org/download/releases/2.2.3/descrintro/#cooperation
Things to watch for when using super
http://fuhm.net/super-harmful/
Pythons MRO Algorithm:
http://www.python.org/download/releases/2.3/mro/
super's docs:
http://docs.python.org/library/functions.html
The bottom of this page has a nice section on super:
http://docstore.mik.ua/orelly/other/python/0596001886_pythonian-chp-5-sect-2.html
I hope this helps clear it up.
只是猜测:
所有四个方法中的
self
都引用同一个对象,即D
类的对象。因此,在
B.__init__()
中,对super(B,self)
的调用知道self
的整个钻石血统,并且它具有从“after”B
获取方法。 在本例中,它是C
类。just guessing:
self
in all the four methods refer to the same object, that is, of classD
.so, in
B.__init__()
, the call to tosuper(B,self)
knows the whole diamond ancestry ofself
and it has to fetch the method from 'after'B
. in this case, it's theC
class.super()
知道完整类层次结构。 这就是 B 的 init 内部发生的事情:这解决了核心问题,
也就是说,在 B 的 init 定义中,
self
是D
的一个实例,从而传达了C
的存在。 例如,C
可以在type(self).__mro__
中找到。super()
knows the full class hierarchy. This is what happens inside B's init:This resolves the central question,
Namely, in B's init definition,
self
is an instance ofD
, and thus communicates the existence ofC
. For exampleC
can be found intype(self).__mro__
.Jacob 的回答展示了如何理解问题,batbrat 的回答展示了细节,hrr 的回答则开门见山。
他们没有从你的问题中涵盖(至少没有明确)的一件事是:
要理解这一点,请将 Jacob 的代码更改为在 A 的 init 上打印堆栈,如下所示:
看到
B
的行super(B, self).__init__( )
实际上是调用C.__init__()
,因为C
不是B
的基类。发生这种情况是因为
super (B, self)
没有“调用 B 的基类版本的__init__
”。 相反,它是在self
的上的
并且具有这样的属性。B
右侧的第一个类上调用__init__
__mro__因此,如果您在 B 的 init 函数中注释掉对 super 的调用,方法堆栈将在 B.__init__ 处停止,并且永远不会到达 C > 或
A
。总结一下:
self
始终是对实例的引用,并且其__mro__
和__class__
保持常量__mro__
上当前类右侧的类的方法。 由于 __mro__ 保持不变,因此它会作为列表而不是树或图进行搜索。关于最后一点,请注意,MRO 算法的全名是C3 超类线性化。 也就是说,它将该结构扁平化为列表。 当不同的
super()
调用发生时,它们会有效地迭代该列表。Jacob's answer shows how to understand the problem, while batbrat's shows the details and hrr's goes straight to the point.
One thing they do not cover (at least not explicity) from your question is this point:
To understand that, change Jacob's code to to print the stack on A's init, as below:
It is a bit surprising to see that
B
's linesuper(B, self).__init__()
is actually callingC.__init__()
, asC
is not a baseclass ofB
.This happens because
super (B, self)
is not 'calling the B's baseclass version of__init__
'. Instead, it is 'calling__init__
on the first class to the right ofB
that is present onself
's__mro__
and that has such an attribute.So, if you comment out the call to super in B's init function, the method stack will stop on
B.__init__
, and will never reachC
orA
.To summarize:
self
is always a reference to the instance, and its__mro__
and__class__
remain constant__mro__
. As the__mro__
remains constant, what happens is that it is searched as a list, not as a tree or a graph.On that last point, note that the full name of the MRO's algorithm is C3 superclass linearization. That is, it flattens that structure into a list. When the different
super()
calls happen, they are effectivelly iterating that list.