Python 3 内置类型 __init__ 不调用 super().__init__?
当我们从一个内置类型和其他类同时继承时,发现内置类型的构造函数不会调用父类的构造函数。这就导致在方法解析顺序(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 个回答
使用 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
)。如果想了解更多,可以看看这些资料:
- Raymond Hettinger 的 Python 的 super() 被认为是超级的!
- James Knight 的 Python 的 Super 很不错,但你不能用它