有些图书馆确实利用了元数据。例如,marshmallow 是一个非常流行的数据类验证库,它允许您通过在您自己定义的数据类中使用元数据挂钩来安装自定义验证器方法和其他一些东西。我想其他想要扩展数据类可用性的库也可以利用它。
是的,您的猜测是您不想/不能使用元数据来跟踪是否计算了一个字段,这对我来说听起来是正确的。我认为您可以采取一些技巧来完成您想做的事情,但听起来您想要一个比数据类更有状态的类,并且它们的字段的元数据功能“应该”是。
由于映射代理是只读的,因此在实例化数据类时首次创建它后,您没有太多更新它的能力。解决元数据字段的只读限制的最灵活的方法是让元数据字段本身指向数据类实例本身范围之外的东西。在这种情况下,只读包装器围绕着您所指向的事物,而不是事物中的内容。或者,您也可以在运行时自己更新该类的整个元数据字段(这是允许的,因为它是只读的元数据内容,而不是元数据字段本身)。
但是,如果您更匿名地创建它,您将无法在创建时间之后更改元数据字段的样子。
例如:
from dataclasses import dataclass, field, fields
external_metadata_dict = {'is_initialized': False}
other_external_metadata_dict = {'foo': 1}
@dataclass
class UsesExternalDict:
variable: int = field(metadata=external_metadata_dict)
example1 = UsesExternalDict(0)
print(f'example1 initial metadata: {fields(example1)[0].metadata}')
example2 = UsesExternalDict(0)
print(f'example2 initial metadata: {fields(example2)[0].metadata}')
# update the thing example1 and example2 both point to, even though their metadata is in a read-only wrapper
external_metadata_dict['is_initialized'] = True
print(f'example1 updated metadata: {fields(example1)[0].metadata}')
print(f'example2 updated metadata: {fields(example2)[0].metadata}')
# directly modifying the 'metadata' field also allowed
example3 = UsesExternalDict(0)
fields(example3)[0].metadata = other_external_metadata_dict
给予
example1 initial metadata: {'is_initialized': False}
example2 initial metadata: {'is_initialized': False}
example1 updated metadata: {'is_initialized': True}
example2 updated metadata: {'is_initialized': True}
example3 initial metadata: {'is_initialized': True}
example3 updated metadata: {'foo': 1}
但在大多数编程情况下,您最有可能希望这样做:
from dataclasses import dataclass, field, fields
@dataclass
class UsesInternalDict:
variable: int = field(metadata={'is_initialized': False})
example = UsesInternalDict(0)
print(f'example initial metadata: {fields(example)[0].metadata}')
fields(example)[0].metadata['is_initialized'] = True
print(f'example updated metadata: {fields(example)[0].metadata}')
只是给
example initial metadata: {'is_initialized': False}
Traceback (most recent call last):
File "/home/sinback/scrap.py", line 11, in <module>
fields(example)[0].metadata['is_initialized'] = True
TypeError: 'mappingproxy' object does not support item assignment
因为您正在尝试更新数据类的实际只读部分。
没有什么阻止您编写数据类的方法,该方法在运行时手动更新其中一个字段的元数据部分,采用我在第一个示例中演示的方式之一,例如:
@dataclass
class UsesExternalDict:
variable: int = field(metadata=external_metadata_dict)
def messwith(self):
self.__dataclass_fields__['variable'].metadata = other_external_metadata_dict
但是...它在风格上确实不符合数据类的工作方式。即使撇开风格问题,它也是一个非常脆弱和粗略的解决方案——它利用了 Python 的一切都是参考语言的特性,这会导致程序员一直编写错误。如果你和其他人一起工作,这完全会导致混乱。
您可以探索的另一种选择是让您自己跟踪的字段成为其自己的类,该类自己跟踪该元数据。对我来说,这对你来说是最好的选择,但这取决于你。