python - 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
,而不是一些自定义超类。
解决方案
除了验证参数计数和调用(除了一些健全性检查之外什么都不做)之外,该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__
实际上不会改变类字典。
推荐阅读
- python - 插入值作为参数 - Python
- python - Keras Conv1D 或 Convolution1D
- c# - C# 使用 jdbc url 连接到 oracle db
- python - 带有venv的django 2.1环境
- php - PHP 和 AJAX url 获取数据
- apache-kafka - Kafka Streams KTable Store 在这种情况下对于压缩输入主题没有用处,替代方案?
- google-apps-script - 将二维数组传递给创建另一个维度的函数
- latex - 在 LaTeX 中创建特定表
- r - R Markdown,确保代码输出在多个页面之间不被打断
- laravel - 在 laravel 5.4 中显示图像