一个关于C3的例子

an example about C3

我从github那里得到了关于MRO和C3的代码,最后三行我不太明白,super().foo(), super(B ,self).foo()和super(C,self).foo() in python3.x,代码如下:

class A(object):

    def foo(self):
        print('foo of A')

class B(A):
    pass

class C(A):

    def foo(self):
        print('foo fo C')

class D(B, C):
    pass

class E(D):

    def foo(self):
        print('foo in E')
        super().foo()
        super(B, self).foo()
        super(C, self).foo()

if __name__ == '__main__':
    d = D()
    d.foo()
    e = E()
    e.foo()

预期和实际结果如下:

foo fo C
foo in E
foo fo C
foo fo C
foo of A    

首先,Python3中的形式super()super(<CurrentClass>, self)其实是一回事,其中Python编译器provides enough information for super() to determine what the correct class to use is。所以在E.foo()中,super().foo()可以读作super(E, self).foo()

要了解发生了什么,您需要查看 class.__mro__ attribute:

This attribute is a tuple of classes that are considered when looking for base classes during method resolution.

正是这个元组向您展示了 C3 方法解析顺序 对于任何给定的 class 层次结构。对于您的 class E,该顺序是:

>>> E.__mro__
(<class '__main__.E'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
>>> for cls in E.__mro__:  # print out just the names, for easier readability.
...     print(cls.__name__)
...
E
D
B
C
A
object

super() 对象的所有内容都基于 classes 的有序序列。来电

super(SomeClass, self).foo()

导致以下一系列步骤:

  • super() 对象检索 self.__mro__ 元组。
  • super() 在该元组中找到 SomeClass class 的索引。
  • 访问 super() 对象上的 foo 属性会触发对 class 的搜索,该 class 在 MRO 上具有 foo 属性,开始在 SomeClass 索引 .
  • 之后的下一个索引处
  • 如果以这种方式找到的属性是 descriptor object,则将以这种方式找到的属性绑定到 self。函数是描述符,绑定产生绑定方法,调用方法时Python就是这样传入self引用的。

表示为简化的 Python 代码,忽略边缘情况和 super() 的其他用途,看起来像:

class Super:
    def __init__(self, type_, obj_or_type):
        self.mro = obj_or_type.__mro__
        self.idx = self.mro.index(type_) + 1
        self.obj_or_type = obj_or_type
    def __getattr__(self, name):
        for cls in self.mro[self.idx:]:
            attrs = vars(cls)
            if name in attrs:
                result = attrs[name]
                if hasattr(result, '__get__'):
                    result = result.__get__(obj_or_type, type(self.obj_or_type))
                return result
        raise AttributeError(name)

结合这两条信息,您可以看到调用 e.foo():

时会发生什么
  • print('foo in E') 被执行,导致 E
  • 中的 foo
  • super().foo() 被执行,实际上与 super(E, self).foo() 相同。
    • 搜索MRO,从下一个索引过去E开始,所以在D(没有foo属性),移动到 B(没有 foo 属性),然后是 C(找到属性)。返回 C.foo,绑定到 self
    • C.foo(self) 被调用,导致 foo fo C
  • super(B, self).foo() 被执行。
    • 搜索 MRO,从下一个索引 过去 B 开始,所以在 C(找到属性)。返回 C.foo,绑定到 self
    • C.foo(self) 被调用,导致 foo fo C
  • super(C, self).foo() 被执行。
    • 搜索 MRO,从下一个索引 过去 C 开始,所以在 A(找到属性)。返回 A.foo,绑定到 self
    • A.foo(self) 被调用,导致 A
    • foo