首页 > 解决方案 > Python 的内置 type.__init__(name,bases,dct) 有什么作用吗?

问题描述

我见过一些 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__的。据我所知,这没有任何作用。它有什么作用吗?您是否有理由想要包含它?Klass和之间有什么有效区别Klass2吗?

注意:我说的是Meta继承自的情况type,而不是一些自定义超类。

标签: pythonmetaclasspython-internals

解决方案


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

但是,对于继承自其他 mixin 元类并且必须考虑其他 mixin 元类的元类, usingsuper().__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'})

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

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

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


推荐阅读