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

时间:2019-08-15 20:14:39

标签: python metaclass python-internals

我已经看到一些Python元类示例使用对test(*value(1,2)) # >>> test(1, 2) 的{​​{1}}调用。这是做什么的?

示例:

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' 。据我所知,这没有任何作用。它有作用吗?您可能有理由要包含它吗? dctKlass.__dict__之间有什么有效区别吗?

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

2 个答案:

答案 0 :(得分:5)

type.__init__() implementation确实不执行任何操作,除了验证参数计数并调用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'})

请注意最后调用MetaMixin()的方式(在调用type.__init__()之前)。如果MetaMixin.__init__()会参考namespace字典以供自己使用,则更改namespace中的MetaFoo.__init__()将会更改MetaMixin.__init__()在该字典中的查找结果。

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

在将namespace字典参数(您使用名称dct)用作类属性字典之前,已将它们复制 ,因此在{{1 }}确实不会改变类字典。

答案 1 :(得分:2)

type.__init__仅确保:

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

name方法实际上未使用basesdct__init__中的任何一个。除了初始调用已经做过的事情之外,没有其他副作用(例如,检查参数的有效性)。

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