理解python的超级方法,为什么D()。test()将返回'B-> C'而不是'B - > A'

时间:2013-09-11 00:35:32

标签: python super

我在这里看了关于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()有两个参数:a   class和该类的实例。正如我们在此处的示例所示,实例对象确定   哪个MRO将用于解析结果对象的任何属性。提供的类确定   该MRO的一个子集,因为super()仅使用在类之后发生的MRO中的那些条目   提供。

我仍然觉得解释有点难以理解。这可能是一个可能的重复,并且已经多次询问过与此类似的问题,但如果我对此有所了解,我可能会更好地理解其他问题。

Understanding Python super() with __init__() methods

What does 'super' do in Python?

python, inheritance, super() method

[python]: confused by super()

3 个答案:

答案 0 :(得分:5)

如果您想知道Python选择此特定MRO算法的原因,请在mailing list archives中进行讨论,并在The Python 2.3 Method Resolution Order中进行简要总结。

但实际上,它归结为:Python 2.2的方法解决方案在处理多重继承时被打破,并且任何人建议修复它的第一件事是从Dylan借用C3算法,没有人遇到任何问题它或建议更好的东西,因此Python使用C3。


如果您对C3对其他算法的一般优势(和劣势)更感兴趣......

BrenBarn和florquake的答案给出了这个问题的基础知识。来自Raymond Hettinger博客的Python's super() considered super!是一个更长,更详细的讨论,绝对值得一读。

A Monotonic Superclass Linearlization for Dylan是描述设计的原始论文。当然Dylan是一种与Python非常不同的语言,这是一篇学术论文,但其基本原理仍然相当不错。

最后,The Python 2.3 Method Resolution Order(上面链接的相同文档)对这些好处进行了一些讨论。

你需要学习很多关于替代品的知识,以及他们是如何以及不适合Python的,以便更进一步。或者,如果您想了解有关SO的更深入信息,您需要提出更具体的问题。


最后,如果您问的是“如何”问题:

当您致电D().test()时,它显然会调用您在B的{​​{1}}方法中定义的代码。 testB.__mro__。那么,(__main__.B, __main__.A, object)如何调用super(B, self).test()的{​​{1}}方法代替C呢?

这里的关键是MRO基于test的类型,而不是基于定义A方法的类型self。如果您在B函数内test print(type(self)),则会看到它是test,而不是D

因此,B实际获得super(B, self)(在本例中为self.__class__.__mro__),在列表中找到D.__mro__,然后返回其后的内容。非常简单。

但这并没有解释MRO是如何运作的,只是它的作用。 B如何从D().test()调用方法,但Bself

首先,请注意DD().testD.test不是同一个函数,因为它们根本不是函数;他们是方法。 (我在这里假设Python 2.x。事情有点不同 - 主要是在3.x中更简单。)

方法基本上是具有B.testim_funcim_class成员的对象。当你调用一个方法时,你所做的就是调用它的im_self,其im_func(如果不是im_self)在开始时作为额外的参数填入。

因此,我们的三个示例都具有相同的None,其中 是您在im_func中定义的函数。但前两个对BD而不是B,第一个对im_classD个实例而不是None。因此,调用它最终将im_self实例作为D传递。

那么,self如何以D().testim_self结束?这会在哪里创建?这是有趣的部分。有关完整说明,请阅读Descriptor HowTo Guide,但请简要说明一下:

每当你写im_class时,实际发生的事情等同于对foo.bar的调用,它会执行类似这样的操作(忽略实例属性,getattr(foo, 'bar')__getattr__,插槽,builtins等):

__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本身,im_func作为类im_classobj.__class__作为对象im_self。< / p>

答案 1 :(得分:4)

简短的回答是方法解析顺序大致为#34;广度优先&#34;。也就是说,在进入任何超类之前,它会遍历给定级别的祖先的所有基类。因此,如果D继承自B和C,它们都继承自A,那么MRO在A之前总是具有B C.

另一种思考方式是,如果订单变为B-> A,那么A.test将在C.test之前被调用,即使CA的子类1}}。您通常希望在超类之前调用​​子类实现(因为子类可能希望完全覆盖超类而根本不调用它)。

可以找到更长的解释here。您还可以通过Google搜索或搜索Stackoverflow以查找有关&#34; Python方法解析顺序&#34;的问题。或者&#34; Python MRO&#34;。

答案 2 :(得分:3)

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!

你希望你的DuckPigquack!oink!,毕竟它是猪和鸭,对吧?嗯,这就是super()的用途。