【问题标题】:Python class inheritance call orderPython类继承调用顺序
【发布时间】:2018-03-13 04:51:20
【问题描述】:

有一个著名的 Python 例子

class A(object):
    def go(self):
        print("go A go!")

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

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

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


d = D()
d.go() 

#go A go!
#go C go!
#go B go!
#go D go!

我有几个问题。第一个是 B 调用 A 而 C 调用 A 所以我希望 A 出现两次。第二个问题是关于订单的。

【问题讨论】:

    标签: python class inheritance


    【解决方案1】:

    从 Python 2.3 开始,方法解析使用了一种称为 C3 线性化的算法(借用自 Dylan)。维基百科有a nice article on it

    顾名思义,这个想法是强制方法解析图是一条直线,即使继承图不是。这意味着 A 在设计上不会出现两次。

    为什么?好吧,一方面,它完全避免了困扰许多其他语言的多重继承的“钻石问题”。 (或者更准确地说,许多其他语言要么禁止 MI,要么将其限制为纯“接口”,因为它们没有解决 C++ 中存在的问题的方法。)

    原始 Python 解释(包括其背后的动机)可在 The Python 2.3 Method Resolution Order 中找到。这有点技术性,但如果您有兴趣,值得一读。

    您可能还想阅读the original Dylan paper,它更详细地介绍了非线性 MRO 图的问题,以及提出单调线性化的挑战(即,它按照您期望的顺序进行,或者至少是你在克服它是线性的事实后所期望的顺序),等等。

    如果您想更深入地了解 type() 如何在幕后工作,或者只是想看看 2.3 和 3.7 之间发生了什么变化(例如,__mro__ 的创建和更新方式 - 尽管魔法 3.x @ 987654328@ 在别处),真的没有比the CPython source 更好的地方了。

    【讨论】:

    • 我认为 2.2 也是线性的,只是不是单调线性的——或者在没有计算出你头脑中的算法的情况下所有这些都是可预测的——但已经很长时间了,所以我不确定是否可以把它在答案中。
    • 那订单呢?
    • 怎么样?任何链接的文章都解释了用于生成订单的算法,以及为什么生成的订单很好。
    【解决方案2】:

    super 类不只是恢复超类。它实例化一个对象,该对象在给定的方法解析顺序的上下文中恢复方法。每个类都有一个mro,您可以通过__mro__ 属性访问它。

    D.__mro__ # (D, B, C, A, object)
    

    所以当给定一个类和一个实例时,super 首先从那个实例中恢复 mro。当您尝试从 super 对象中恢复属性时,它会从提供的具有此类属性的类之后的第一个类中返回该属性。

    如果你要在 Python 中实现 super 的行为,它看起来像这样。

    class super:
        def __init__(self, cls, instance):
            if not isinstance(cls, type):
                raise TypeError('super() argument 1 must be type')
            if isinstance(instance, cls):
                self.mro = type(instance).__mro__
            elif isinstance(instance, type) and issubclass(instance, cls):
                self.mro = instance.__mro__
            else:
                raise TypeError('super(type, obj): obj must be an instance or subtype of type')
    
            self.cls = cls
            self.instance = instance
    
        def __getattr__(self, attr):
            cls_index = self.mro.index(self.cls)
    
            for supercls in self.mro[cls_index + 1:]:
                if hasattr(supercls, attr): break
    
            # The actual implementation binds instances to methods before returning
            return getattr(supercls, attr)
    

    回到你的例子,当你调用super(B, self).go时,它会恢复self__mro__,它的类型是D。然后,它从 mroB 之后的第一个类中选择 go,该类具有此类属性。

    所以在这种情况下,由于self.__mro__(D, B, C, A, object),所以B 之后具有go 属性的第一个类是C 而不是A

    如果您想了解 Python 如何确定 mro 的详细信息,那么我建议您使用abarnert's answer

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-22
      • 1970-01-01
      • 2016-11-21
      • 2016-08-09
      • 2013-01-29
      相关资源
      最近更新 更多