8

我是 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”,而是关于 .MRO 列表中的下一个类C。但是B在 MRO 列表中没有C.

案例二:

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 会受到怎样的影响?

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

4

1 回答 1

5

为了解决这些问题,您首先需要了解MROsuper()是如何工作的。

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

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

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

因此,super()将根据MRO ( classobject.__mro__) 进行搜索,它会被父类或同级类满足。

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

情况 2
D 的 MRO 与情况 1 中的 D 相同。 ( (D, C, B, A, object))
但是,super()将在 C 处停止或满足,因为在Csave()类中没有实现超函数调用。

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

此外,如果将 D 的继承顺序从 C, B 切换到 B, C. ( class D(C, B)to class D(B, C))。然后,当您调用 时D().save(),输出将是DB,因为D.__mro__ = (D, B, C, A, object)并且 super() 将在 B 类中得到满足。

此外,据我了解,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() 调用。

于 2017-06-14T03:49:53.220 回答