Python 3 内置类型 __init__ 不调用 super().__init__?

15 投票
1 回答
3584 浏览
提问于 2025-04-17 10:32

当我们从一个内置类型和其他类同时继承时,发现内置类型的构造函数不会调用父类的构造函数。这就导致在方法解析顺序(MRO)中,后面的类型的 __init__ 方法不会被调用。

举个例子:

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__ 被调用了)。

这种对继承顺序的微妙依赖看起来有点不符合 Python 的风格,这样设计是故意的吗?这也意味着在复杂的类层次结构中,你绝对不能从内置类型继承,因为你无法知道当其他人从你的类继承时,内置类型在 MRO 中的位置(这会让维护变得很麻烦)。我是不是漏掉了什么?

补充信息:Python 版本 3.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__(),如果所有构造函数的参数格式不统一,那也没什么用。所以你说得对,它们不适合参与这种合作式的构造函数调用。

只有当所有参与的方法参数格式一样(比如 __setattr__())或者你使用上面提到的(或类似的)写法时,才能使用 super()。不过,使用 super() 并不是调用父类方法的唯一方式。如果你的多重继承中没有“菱形”问题,你可以直接调用父类的方法,比如 B.__init__(self, param_a)。有多个父类的类可以直接调用多个构造函数。即使有“菱形”问题,有时候你也可以用直接调用,只要注意 __init__() 可能会被调用多次而不会出错。

如果你还是想在构造函数中使用 super(),那么确实不应该在多重继承中使用内置类型的子类(除了 object)。如果想了解更多,可以看看这些资料:

撰写回答