钻石继承和MRO

时间:2017-03-08 07:15:33

标签: python method-resolution-order

我是MRO的新手,并且在确定这些输出的逻辑时遇到了问题。

案例1:

class A(object):
  def save(self):
      print "A"

class B(A):
    def save(self):
        print "B"
        super(B, self).save()

class C(A):
  def save(self):
      print "C"
      super(C, self).save()


class D(C, B):
    def save(self):
        print "D"
        super(D,self).save()

D().save()

输出:

D
C
B
A

我的问题是super(C)调用B.save()的方式。

根据MRO:super(C, self)不是关于“C的基类”,而是关于C的MRO列表中的下一个类。但是B的MRO列表中没有C

案例2:

class A(object):
  def save(self):
      print "A"

class B(A):
    def save(self):
        print "B"
        super(B, self).save()

class C(A):
  def save(self):
      print "C"
      # removed super call here

class D(C, B):
    def save(self):
        print "D"
        super(D,self).save()

D().save()

输出:

D
C

案例3:

class A(object):
  def save(self):
      print "A"

class B(object):
    #inherits object now instead of A
    def save(self):
        print "B"
        super(B, self).save()

class C(A):
  def save(self):
      print "C"
      super(C, self).save()

class D(C, B):
    def save(self):
        print "D"
        super(D,self).save()

D().save()

输出:

D
C
A

问题

如果B不是从A继承,而是直接从object继承,那么MRO如何受到影响?

有人可以解释这背后的原因吗?

1 个答案:

答案 0 :(得分:2)

为了解决这些问题,首先需要了解 MRO super()的工作原理。

MRO Python Document - MRO
在Python中,MRO基于C3线性化。 (wiki和python文档中有很多例子)

super()(查看Python文档 - super())
根据Python文档,它说..

  

返回一个代理对象,该方法将方法调用委托给父类型的兄弟类。这对于访问已在类中重写的继承方法很有用。搜索顺序与getattr()使用的搜索顺序相同,只是跳过了类型本身。

因此, super()将基于 MRO classobject.__mro__)进行搜索,并且它将由父类或兄弟类类型满足。< / p>

回到你的案件,
案例1
D.__mro__ = (D, C, B, A, object)
因此,输出将为D C B A

案例2
D的MRO与案例1中的D相同。((D, C, B, A, object)
但是, super()将在 C 停止或满意,因为C类中的save()没有实现超级函数调用。

案例3
D的MRO等于(D, C, A, B, object)。因此,当 super()在A类中达到save()函数时,它会认为满意。这就是为什么B中的保存功能从未被调用过的原因。

此外,如果您将D的继承序列从C,B切换到B,C。(class D(C, B)class D(B, C))。然后,当您拨打D().save()时,输出将为 D B ,因为{B}会满足D.__mro__ = (D, B, C, A, object)和super()。

此外,根据我的理解, super()不是强制子类对象在父类中调用所有覆盖函数的函数。如果要强制类调用其父类中的所有覆盖函数。您可以尝试执行以下操作:

class Base(object):
    def method(self):
        print('Base-method')

class Parent(object):
    def method(self):
        print('Parent-method')

class Child(Parent):
    def method(self):
        print('Child-method')
        super(Child, self).method()

class GrandChild(Child, Base):
    def method(self):
        print('GrandChild-method')
        for base in GrandChild.__bases__:
            base.method(self)
        #super(GrandChild, self).method()

然后,当你调用`GrandChild()。method()时,输出将为:

GrandChild-method
Child-method
Parent-method
Base-method

注意:
GrandChild.__bases__仅包含类Child和类Base

PS。
通过说上面的满意,我的意思是没有更多的super()调用。