[英]Understanding python's super method, Why D().test() will return 'B->C' and not 'B-->A'
我在这里查看了有关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'
书中的解释是
在最常见的情况下(包括此处显示的用法),super()接受两个参数:一个类和该类的实例。 如我们的示例所示,实例对象确定将使用哪个MRO解析结果对象上的任何属性。 提供的类确定该MRO的子集,因为super()仅使用MRO中提供的类之后出现的那些条目。
我仍然觉得解释有些难以理解。 这可能是重复的,类似的问题已经被问过很多次了,但是如果我对此有所了解,我也许能够更好地理解其他问题。
如果您想知道Python为什么选择这种特定的MRO算法,请在邮件列表档案中进行讨论,并在《 Python 2.3方法解析顺序》中进行了简要概述。
但实际上,可以归结为这一点:处理多重继承时,Python 2.2的方法解析被破坏了,任何人建议修复它的第一件事就是从Dylan借用C3算法,没有人对它有任何问题或建议更好,因此Python使用C3。
如果您对C3相对于其他算法的一般优点(和缺点)更感兴趣...
布伦·巴恩(BrenBarn)和弗洛格克(florquake)的答案为该问题提供了基础。 Python的super()被认为是超级! 同样来自Raymond Hettinger博客的文章讨论的时间更长,更详细,绝对值得一读。
Dylan的单调超类线性化是描述该设计的原始论文。 当然,Dylan是与Python截然不同的语言,这是一篇学术论文,但是原理仍然很不错。
最后, Python 2.3方法解决顺序 (与上面链接的文档相同)对收益进行了一些讨论。
而且,您还需要学习很多关于替代方法的知识,以及它们与Python相适应的方式和不适合的方法,以进一步发展。 或者,如果您想获得有关SO的更深入信息,则需要提出更具体的问题。
最后,如果您要问“如何”问题:
当您调用D().test()
,显然是在调用您在B
的test
方法中定义的代码。 B.__mro__
是(__main__.B, __main__.A, object)
。 那么, super(B, self).test()
怎么可能调用C
的test
方法而不是A
的呢?
这里的关键是MRO是基于self
的类型,而不是基于定义test
方法的B
类型。 如果要在test
函数中进行print(type(self))
,则将看到它是D
,而不是B
因此, super(B, self)
实际上获得了self.__class__.__mro__
(在本例中为D.__mro__
),在列表中找到B
,然后返回其后的下一个对象。 很简单。
但这并不能解释MRO的工作原理,而只是解释它的工作原理。 D().test()
如何从B
调用方法,但self
为D
?
首先,请注意D().test
, D.test
和B.test
是不相同的函数,因为它们根本不是函数。 他们是方法 。 (我在这里假设使用Python2.x。在3.x中,情况有所不同-主要是更简单。)
方法基本上是具有im_func
, im_class
和im_self
成员的对象。 调用方法时,您所要做的就是调用im_func
,并将im_self
(如果不是None
) im_self
为开头的额外参数。
因此,我们的三个示例都具有相同的im_func
,实际上是您在B
内部定义的函数。 但前两个有D
,而不是B
的im_class
,和第一也有D
实例,而不是None
的im_self
。 因此,这就是调用它最终将D
实例作为self
传递的方式。
那么, D().test
如何以那个im_self
和im_class
? 在哪里创建的? 那是有趣的部分。 有关完整的描述,请阅读《 描述符操作指南》 ,但要简短:
每当您编写foo.bar
,实际发生的情况都等同于对getattr(foo, 'bar')
的调用,该调用会执行以下操作(忽略实例属性, __getattr__
getattr(foo, 'bar')
__getattr__
, __getattribute__
,slot,内置插件等):
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
方法。 它要做的是创建一个绑定方法,以im_func
作为自身, im_class
作为obj.__class__
class im_self
, im_self
作为对象obj
。
简短的答案是方法的解析顺序大约是“广度优先”。 也就是说,在进入任何父类之前,它会先经过给定祖先级别的所有基类。 因此,如果D从B和C继承,而B和C都从A继承,则MRO始终在A之前具有B 和 C。
想想它的另一种方式是,如果为了去B-> A,然后A.test
将被调用之前C.test
,即使C
是的子类A
。 通常,您希望在超类之前先调用子类实现(因为子类可能希望完全覆盖超类而不根本不调用它)。
在这里可以找到更长的解释。 您还可以通过搜索或搜索Stackoverflow中有关“ Python方法解析顺序”或“ Python MRO”的问题来找到有用的信息。
super()
基本上就是您告诉Python“执行此对象的其他类说的话”的方式。
当您的每个类只有一个父类(单继承)时, super()
会将您简单地引用到父类。 (我想您已经了解了这一部分。)
但是,当您使用多个基类时,就像您在示例中所做的那样,事情开始变得有点复杂。 在这种情况下,Python确保如果在任何地方都调用super()
,则将调用每个类的方法。
一个(有点荒谬的)示例:
class Animal(object):
def make_sounds(self):
pass
class Duck(Animal):
def make_sounds(self):
print 'quack!'
super(Duck, self).make_sounds()
class Pig(Animal):
def make_sounds(self):
print 'oink!'
super(Pig, self).make_sounds()
# Let's try crossing some animals
class DuckPig(Duck, Pig):
pass
my_duck_pig = DuckPig()
my_duck_pig.make_sounds()
# quack!
# oink!
您可能希望您的DuckPig
说quack!
oink!
毕竟是猪和鸭,对吗? 好吧,这就是super()
目的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.