15

从内置类型以及其他类派生时,似乎内置类型的构造函数不调用超类构造函数。这导致 __init__ 方法不会被 MRO 中内置函数之后的类型调用。

例子:

class A:
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        print("A().__init__()")

class B(list, A):
    def __init__(self, *args, **kwargs):
        print("B().__init__() start")
        super().__init__(*args, **kwargs)
        print("B().__init__() end")

if __name__ == '__main__':
    b = B()

在此示例中,从未调用 A.__init__。当 B 被定义为class B(A, list)——切换继承顺序——它按预期工作(即 A.__init__ 被调用)。

这种对继承顺序的非常微妙的依赖似乎相当不符合pythonic,是这样打算的吗?这也意味着您绝不能从复杂类层次结构中的内置类型派生,因为当其他人从您的类派生时,您无法知道内置类型在 MRO 中的最终位置(维护恐惧)。我错过了什么吗?

额外信息:Python 3.1 版

4

1 回答 1

10

的正确用法super()相当微妙,如果协作方法不都具有相同的签名,则需要小心。方法的通常模式__init__()如下:

class A(object):
    def __init__(self, param_a, **kwargs):
        self.param_a = param_a
        super(A, self).__init__(**kwargs)

class B(A):
    def __init__(self, param_b, **kwargs):
        self.param_b = param_b
        super(B, self).__init__(**kwargs)

class C(A):
    def __init__(self, param_c, **kwargs):
        self.param_c = param_c
        super(C, self).__init__(**kwargs)

class D(B, C):
    def __init__(self, param_d, **kwargs):
        self.param_d = param_d
        super(D, self).__init__(**kwargs)

d = D(param_a=1, param_b=2, param_c=3, param_d=4)

请注意,这要求所有方法协作,并且所有方法都需要某种兼容的签名,以确保调用该方法的时间点无关紧要。

内置类型的构造函数没有允许参与这种协作的构造函数签名。除非所有构造函数签名都统一,否则即使他们确实调用super().__init__()了它也将毫无用处。所以最后你是对的——它们不适合参与协作构造函数调用。

super()仅当所有协作方法具有相同的签名(如 eg __setattr__())或您使用上述(或类似)模式时才能使用。但是,使用super()并不是调用基类方法的唯一模式。如果多重继承模式中没有“钻石”,则可以使用显式基类调用,例如B.__init__(self, param_a). 具有多个基类的类只需调用多个构造函数。即使有菱形,有时您也可以使用显式调用,只要您注意__init__()可能会多次调用 an 而不会造成伤害。

如果您无论如何都想使用构造函数,那么您确实不应该在多个继承层次super()结构中使用内置类型的子类(除了)。object一些进一步的阅读:

于 2012-01-18T18:42:07.007 回答