Python 的内置 type.__init__(name,bases,dct) 有什么作用吗?

Does Python's builtin type.__init__(name,bases,dct) do anything?

我见过一些 Python 元类使用 super() 调用 type.__init__() 的示例。这是做什么的?

示例:

class Meta(type):
    def __new__(cls, name, bases, dct):
        dct['a']='a'
        cls_obj = super(Meta, cls).__new__(cls, name, bases, dct)
        return cls_obj

    def __init__(cls_obj, name, bases, dct):
        cls_obj.b = 'b'
        dct['c'] = 'c'
        #what does this do
        super(Meta, cls_obj).__init__(name, bases, dct)

class Meta2(Meta):
    def __init__(cls_obj, name, bases, dct):
        cls_obj.b = 'b'

class Klass(metaclass=Meta):
    pass

class Klass2(metaclass=Meta2):
    pass


if __name__ == '__main__':
    print(Klass.a)
    print(Klass.b)
    print(Klass.c)

输出:

a
b
<...my system traceback...>
AttributeError: type object 'Klass' has no attribute 'c'

很明显dct不是用来更新Klass.__dict__的。据我所知,这没有任何作用。它有作用吗?您是否有可能想要包含它的原因? KlassKlass2之间有什么有效的区别吗?

注意:我专门讨论 Meta 继承自 type 的情况,而不是某些自定义超类。

type.__init__() implementation does indeed do nothing, other than validate the argument count and calling object.__init__(cls)(除了一些健全性检查之外什么都不做)。

然而,对于从 other mixin metaclasses 继承并必须考虑的元classes,使用super().__init__(name, bases, namespace)确保查阅 MRO 中的所有元classes。

例如当使用多个 metaclasses 作为新 metaclass 的基础时,通过 super().__init__() 调用的内容会发生变化:

>>> class MetaFoo(type):
...     def __init__(cls, name, bases, namespace):
...         print(f"MetaFoo.__init__({cls!r}, {name!r}, {bases!r}, {namespace!r})")
...         super().__init__(name, bases, namespace)
...
>>> class MetaMixin(type):
...     def __init__(cls, name, bases, namespace):
...         print(f"MetaMixin.__init__({cls!r}, {name!r}, {bases!r}, {namespace!r})")
...         super().__init__(name, bases, namespace)
...
>>> class MetaBar(MetaFoo, MetaMixin):
...     def __init__(cls, name, bases, namespace):
...         print(f"MetaBar.__init__({cls!r}, {name!r}, {bases!r}, {namespace!r})")
...         super().__init__(name, bases, namespace)
...
>>> class Foo(metaclass=MetaFoo): pass
...
MetaFoo.__init__(<class '__main__.Foo'>, 'Foo', (), {'__module__': '__main__', '__qualname__': 'Foo'})
>>> class Bar(metaclass=MetaBar): pass
...
MetaBar.__init__(<class '__main__.Bar'>, 'Bar', (), {'__module__': '__main__', '__qualname__': 'Bar'})
MetaFoo.__init__(<class '__main__.Bar'>, 'Bar', (), {'__module__': '__main__', '__qualname__': 'Bar'})
MetaMixin.__init__(<class '__main__.Bar'>, 'Bar', (), {'__module__': '__main__', '__qualname__': 'Bar'})

注意最后调用 MetaMixin() 的方式(在调用 type.__init__() 之前)。如果 MetaMixin.__init__() 查阅 namespace 字典供自己使用,那么在 MetaFoo.__init__() 中更改 namespace 将改变 MetaMixin.__init__() 在该字典中找到的内容。

因此,如果您看到 super() 被用在元 __init__ 方法中 class,您可能需要检查更复杂的元 class 层次结构.或者该项目只是为了安全起见,并确保它们的元classes 可以在更复杂的场景中继承。

namespace 字典参数(您使用的名称 dct)在用作 class 属性字典之前被 复制,所以在 __init__ 中向其添加新键确实不会改变 class 字典。

type.__init__ 仅确保:

  • 没有关键字参数保留 如果正好存在 1 个位置参数,
  • 提供了 1 个或 3 个位置参数,并且
  • object.__init__ 被调用,这
    • 检查调用是否正确。
namebasesdct

None 实际上被 __init__ 方法使用。除了初始调用已经执行的操作(例如检查参数的有效性)之外,没有任何副作用。

因此,跳过super().__init__(name, bases, dct)的调用没有任何负面影响如果超类是type。但是,类似于 类 从 object 继承,调用 super().__init__ 是保持未来发展的正确设计——既适用于继承层次结构的更改,也适用于 type.