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__
的。据我所知,这没有任何作用。它有作用吗?您是否有可能想要包含它的原因? Klass
和Klass2
之间有什么有效的区别吗?
注意:我专门讨论 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__
被调用,这
- 检查调用是否正确。
name
、bases
或 dct
的 None 实际上被 __init__
方法使用。除了初始调用已经执行的操作(例如检查参数的有效性)之外,没有任何副作用。
因此,跳过对super().__init__(name, bases, dct)
的调用没有任何负面影响如果超类是type
。但是,类似于 类 从 object
继承,调用 super().__init__
是保持未来发展的正确设计——既适用于继承层次结构的更改,也适用于 type
.
我见过一些 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
的情况,而不是某些自定义超类。
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__
被调用,这- 检查调用是否正确。
name
、bases
或 dct
的 None 实际上被 __init__
方法使用。除了初始调用已经执行的操作(例如检查参数的有效性)之外,没有任何副作用。
因此,跳过对super().__init__(name, bases, dct)
的调用没有任何负面影响如果超类是type
。但是,类似于 类 从 object
继承,调用 super().__init__
是保持未来发展的正确设计——既适用于继承层次结构的更改,也适用于 type
.