Python类继承调用顺序

时间:2018-03-13 04:51:20

标签: python class inheritance

有一个着名的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出现两次。第二个问题是订单。

2 个答案:

答案 0 :(得分:5)

自Python 2.3以来,方法解析使用了一种名为C3 Linearization(借用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 super在其他地方),但实际上并没有比the CPython source更好的地方。

答案 1 :(得分:1)

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时,它会恢复__mro__ self D,其类型为go。然后,它会从 mro 中具有此类属性的B后的第一个类中选择self.__mro__

因此,在这种情况下(D, B, C, A, object)Bgo后面具有属性C的第一个类是A而不是var dateTokens = "2018-03-13".split("-"); //creating date object from specified year, month, and day var date1 = new Date(dateTokens[0],dateTokens[1] -1,dateTokens[2]); //creating date object from specified date string var date2 = new Date("2018-03-13"); console.log("Date1 in milliseconds: ", date1.getTime()); console.log("Date2 in milliseconds: ", date1.getTime()); console.log("Date1: ", date1.toString()); console.log("Date2: ", date2.toString()); }。

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