【问题标题】:what is field metadata used for in python dataclassespython数据类中使用的字段元数据是什么
【发布时间】:2021-04-14 23:48:10
【问题描述】:

我一直在阅读documentation 以获取 python 数据类和其他网页。字段元数据是只读的,文档说:

Data Classes 根本不使用它,而是作为第三方扩展机制提供的

我对第三方扩展如何使用“来自只读映射的值”感到困惑。这似乎更像是以前会进入软件文档的内容?

我的意图是装饰我的字段,以便我知道是否计算了特定值(字段的一部分),我认为这不是可以通过元数据来实现的,对吧?

【问题讨论】:

    标签: python metadata python-dataclasses


    【解决方案1】:

    有些图书馆确实利用了元数据。例如,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 的一切都是参考语言的特性,这会导致程序员一直编写错误。如果你和其他人一起工作,这完全会导致混乱。

    您可以探索的另一种选择是让您自己跟踪的字段成为其自己的类,该类自己跟踪该元数据。对我来说,这对你来说是最好的选择,但这取决于你。

    【讨论】:

    • 这是一个很好的解释!谢谢你!您能否举例说明建议/最佳实践使用元数据的实例?谢谢
    • 是的,有些图书馆确实利用了它。例如,marshmallow,一个非常流行的数据类验证库,允许您通过在您自己定义的数据类中使用元数据挂钩来安装自定义验证器方法和其他一些东西。我给出的链接给出了一个如何做到这一点的例子。我想其他想要扩展数据类可用性的库也可以利用它。
    猜你喜欢
    • 2014-10-24
    • 1970-01-01
    • 2018-02-16
    • 2011-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-20
    • 2017-01-21
    相关资源
    最近更新 更多