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

发布于 2024-12-28 02:18:08 字数 700 浏览 0 评论 0原文

当从内置类型以及其他类派生时,内置类型的构造函数似乎不会调用超类构造函数。这会导致 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

When deriving from a builtin type as well as from some other class, it seems that the builtin type's constructor doesn't call the super class constructor. This results in __init__ methods not being called for types that come after the builtin in the MRO.

Example:

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()

In this sample, A.__init__ is never called. When B is defined as class B(A, list) instead -- switching the inheritance order -- it works as intended (i.e. A.__init__ is called).

This very subtle dependence on inheritance order seems rather un-pythonic, is it intended this way? It also means that you must never derive from builtin types in complex class hierarchies, because you can't know where the builtin ends up in the MRO when someone else derives from your classes (maintenance horror). Am I missing something?

Extra info: Python version 3.1

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

日记撕了你也走了 2025-01-04 02:18:08

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 除外)。进一步阅读:

The correct usage of super() is rather subtle and requires some care if the collaborating methods don't all have the same signature. The usual pattern for __init__() methods is the following:

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)

Note that this requires that all methods collaborate, and that all methods need a somewhat compatible signature to ensure it does not matter at which point the method is called.

The constructors of built-in types don't have constructor signatures that allow participating in such a collaboration. Even if they did call super().__init__() this would be rather useless unless all the constructor signatures were unified. So in the end you are right -- they are not suitable for particpation in collaborative constructor calls.

super() can only be used if either all collaborating methods have the same signature (like e.g. __setattr__()) or if you use the above (or a similar) pattern. Using super() isn't the only pattern to call base class methods, though. If there are no "diamonds" in your multiple inheritance pattern, you can use explicit base class calls, for example B.__init__(self, param_a). Classes with multiple base classes simply call multiple constructors. An even if there are diamonds, you can sometimes use explicit calls, as long as you take care that an __init__() may be called several times without harm.

If you want to use super() for contructors anyway, you indeed shouldn't use subclasses of built-in types (except for object) in multiple inheirtance hierachies. Some further reading:

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文