Python的内置类型.__ init __(name,bases,dct)有什么作用吗?

T.A*_*.A. 1 python metaclass python-internals

我已经看到了一些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)
Run Code Online (Sandbox Code Playgroud)

输出:

a
b
<...my system traceback...>
AttributeError: type object 'Klass' has no attribute 'c'
Run Code Online (Sandbox Code Playgroud)

显然dct不用于更新Klass.__dict__。据我所知,这没有任何作用。它有作用吗?您可能有理由要包含它吗?有没有之间的任何实际的区别KlassKlass2

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

Mar*_*ers 5

除了验证参数计数和调用(除了一些健全性检查之外,它什么也不做)之外,该type.__init__()实现确实不执行任何object.__init__(cls)操作。

但是,对于继承自并必须考虑其他mixin元类的元类,使用super().__init__(name, bases, namespace)确保可以查阅MRO中的所有元类。

例如,当使用多个元类将新的元类作为基础时,通过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'})
Run Code Online (Sandbox Code Playgroud)

注意如何MetaMixin()称为last(在type.__init__()调用之前)。如果MetaMixin.__init__()是咨询namespace字典自己的用途,然后改变namespaceMetaFoo.__init__()会改变什么MetaMixin.__init__()在字典中找到。

因此,对于看到super()__init__元类方法中使用的情况,您可能需要检查更复杂的元类层次结构。或者该项目只是为了安全起见,并确保可以在更复杂的场景中继承其元类。

在将namespace字典参数(您使用过name dct)用作类属性字典之前,已将其复制,因此,向其中添加新键__init__实际上不会更改类字典。