我已经看到一些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'
。据我所知,这没有任何作用。它有作用吗?您可能有理由要包含它吗? dct
和Klass.__dict__
之间有什么有效区别吗?
注意:我是专门讨论Klass
继承自Klass2
而不是某些自定义超类的情况。
答案 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__
仅确保:
object.__init__
被称为
name
方法实际上未使用bases
,dct
或__init__
中的任何一个。除了初始调用已经做过的事情之外,没有其他副作用(例如,检查参数的有效性)。
因此,如果超类为super().__init__(name, bases, dct)
,则对type
的调用跳过没有任何负面影响。但是,类似于从object
继承的类,调用super().__init__
是保持面向未来的正确设计-既可以更改继承层次结构也可以更改type
。