【问题标题】:an example about C3一个关于 C3 的例子
【发布时间】:2019-02-18 22:20:19
【问题描述】:

我从github上得到了以下关于MRO和C3的代码,最后三行不太明白,super().foo()、super(B,self).foo( ) 和 super(C,self).foo() 在 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    

【问题讨论】:

  • 这里是E.__mro__(<class 'SO54756219.E'>, <class 'SO54756219.D'>, <class 'SO54756219.B'>, <class 'SO54756219.C'>, <class 'SO54756219.A'>, <class 'object'>)

标签: python python-3.x method-resolution-order


【解决方案1】:

首先,Python 3 中的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

此属性是在方法解析期间查找基类时考虑的类元组。

正是这个元组向您展示了 C3 方法解析顺序对于任何给定的类层次结构是什么。对于您的班级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() 对象将所有内容都基于该有序的类序列。来电

super(SomeClass, self).foo()

导致以下一系列步骤:

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

表示为忽略边缘情况和super() 的其他用途的简化 Python 代码,如下所示:

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') 被执行,导致 foo in E
  • 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) 被调用,导致 foo of A

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-06
    • 2019-07-08
    • 1970-01-01
    相关资源
    最近更新 更多