我在这里查看了有关 python 的 super() 方法的其他问题,但我仍然发现很难理解整个概念。
我也在看 pro python 书中的例子
那里引用的例子是
class A(object):
def test(self):
return 'A'
class B(A):
def test(self):
return 'B-->' + super(B, self).test()
class C(A):
def test(self):
return 'C'
class D(B, C):
pass
>>> A().test()
'A'
>>> B().test()
'B-->A'
>>> C().test()
'C'
>>> D().test()
'B-->C'
>>> A.__mro__
(__main__.A, object)
>>> B.__mro__
(__main__.B, __main__.A, object)
>>> C.__mro__
(__main__.C, __main__.A, object)
>>> D.__mro__
(__main__.D, __main__.B, __main__.C, __main__.A, object)
为什么做 D().test() 我们得到的输出是 'B-->C' 而不是 'B-->A'
书中的解释是
In the most common case, which includes the usage shown here, super() takes two arguments: a class and an instance of that class. As our example here has shown, the instance object determines which MRO will be used to resolve any attributes on the resulting object. The provided class determines a subset of that MRO, because super() only uses those entries in the MRO that occur after the class provided.
我仍然觉得解释有点难以理解。这可能是重复的,类似的问题已经被问过很多次了,但是如果我理解了这一点,我可能能够更好地理解其他问题。
Understanding Python super() with __init__() methods
What does 'super' do in Python?
python, inheritance, super() method
[python]: confused by super()
请您参考如下方法:
如果你想知道 Python 为什么选择这个特定的 MRO 算法,讨论在 mailing list archives ,并在 The Python 2.3 Method Resolution Order 中进行了简要总结.
但实际上,归根结底是:Python 2.2 的方法解析在处理多重继承时被破坏了,有人建议修复它的第一件事是向 Dylan 借用 C3 算法,没有人对此有任何问题或建议更好,因此 Python 使用 C3。
如果您对 C3 相对于其他算法的一般优势(和劣势)更感兴趣……
BrenBarn 和 florquake 的回答为这个问题提供了基础。 Python's super() considered super!来自 Raymond Hettinger 的博客是一个更长更详细的讨论,绝对值得一读。
A Monotonic Superclass Linearlization for Dylan是描述设计的原始论文。当然,Dylan 是一种与 Python 非常不同的语言,这是一篇学术论文,但基本原理仍然很不错。
最后,The Python 2.3 Method Resolution Order (上面链接的相同文档)对好处进行了一些讨论。
你需要了解很多关于替代方案的知识,以及它们如何适合和不适合 Python,才能走得更远。或者,如果您想更深入地了解 SO,您需要提出更具体的问题。
最后,如果您要问“如何”的问题:
当您调用 D().test() ,显然是在调用你在 B 中定义的代码的test方法。和B.__mro__是 (__main__.B, __main__.A, object) .那么,怎么可能super(B, self).test()可能调用C的test方法而不是 A的?
这里的关键是 MRO 基于 self 的类型。 , 不是基于类型 B test方法被定义。如果你是 print(type(self))里面test函数,你会看到它是 D ,而不是 B .
所以,super(B, self)实际得到 self.__class__.__mro__ (在本例中为 D.__mro__),找到 B在列表中,并返回它之后的下一件事。相当简单。
但这并不能解释 MRO 的工作原理,只是它的作用。 D().test()如何从 B 调用方法, 但带有 self那是 D ?
首先,请注意 D().test , D.test和 B.test不是同一个函数,因为它们根本不是函数;他们是方法。 (我假设这里是 Python 2.x。3.x 中的情况有点不同——主要是更简单。)
方法基本上是一个带有 im_func 的对象。 , im_class , 和 im_self成员。当你调用一个方法时,你所做的只是调用它的im_func , 及其 im_self (如果不是 None )在开始时作为额外的参数塞进去。
所以,我们的三个例子都有相同的 im_func ,这实际上是您在 B 中定义的函数.但是前两个有D而不是 B对于 im_class ,第一个也有 D实例而不是 None对于 im_self .所以,这就是调用它最终通过 D 的方式。实例为 self .
那么,D().test 怎么样?最终得到 im_self和 im_class ?这是在哪里创建的?这就是有趣的部分。有关完整说明,请阅读 Descriptor HowTo Guide ,但简要地说:
每当你写 foo.bar , 实际发生的相当于调用 getattr(foo, 'bar') ,它会做这样的事情(忽略实例属性、__getattr__、__getattribute__、插槽、内置函数等):
def getattr(obj, name):
for cls in obj.__class__.__mro__:
try:
desc = cls.__dict__[name]
except KeyError:
pass
else:
return desc.get(obj.__class__, obj)
那
.get()最后是魔术位。如果你查看一个函数——比如说,
B.test.im_func ,你会看到它实际上有一个
get方法。它的作用是创建一个绑定(bind)方法,使用
im_func作为它自己,
im_class作为类(class)
obj.__class__ , 和
im_self作为对象
obj .
