【问题标题】:How to use enum value in asdict function from dataclasses module如何在数据类模块的 asdict 函数中使用枚举值
【发布时间】:2020-08-03 21:42:37
【问题描述】:

我有一个数据类,其字段 template 类型为枚举。使用 asdict 函数时,它将我的数据类转换为字典。是否可以使用FoobarEnumvalue 属性来返回字符串值而不是Enum 对象?

我最初的想法是使用asdict 函数的dict_factory=dict 参数并提供我自己的工厂,但我不知道该怎么做。

from dataclasses import dataclass, asdict
from enum import Enum


@dataclass
class Foobar:
  name: str
  template: "FoobarEnum"


class FoobarEnum(Enum):
  FIRST = "foobar"
  SECOND = "baz"


foobar = Foobar(name="John", template=FoobarEnum.FIRST)

print(asdict(foobar))

当前输出:

{'name': 'John', 'template': <FoobarEnum.FIRST: 'foobar'>}

目标:

{'name': 'John', 'template': 'foobar'}

【问题讨论】:

    标签: python enums python-dataclasses


    【解决方案1】:

    这不能用标准库来完成,除非我不知道一些元类枚举黑客。 Enum.nameEnum.value 是内置的,不应更改。

    使用数据类default_factory 的方法也行不通。因为调用default_factory 是为了为数据类成员生成默认值,而不是自定义对成员的访问。

    您可以将 Enum 成员或 Enum.value 作为数据类成员,这就是 asdict() 将返回的内容。

    如果您想保留一个 Enum 成员 - 而不仅仅是 Enum.value- 作为数据类成员,并且有一个函数将其转换为返回 Enum.value 而不是 Enum 成员的字典,那么正确的方法是正在实现您自己的方法以将数据类作为字典返回。

    from dataclasses import dataclass
    from enum import Enum
    
    
    class FoobarEnum(Enum):
        FIRST = "foobar"
        SECOND = "baz"
    
    
    @dataclass
    class Foobar:
        name: str
        template: FoobarEnum
    
        def as_dict(self):
            return {
                'name': self.name,
                'template': self.template.value
            }
    
    
    # Testing.
    print(Foobar(name="John", template=FoobarEnum.FIRST).as_dict())
    # {'name': 'John', 'template': 'foobar'}
    
    

    【讨论】:

    • 如果您将 Foobar 数据类的实例作为另一个数据类的嵌套实例,则此方法将不起作用。在这种情况下,您需要手动进行对话并在所有实体上调用您的自定义 as_dict 方法。您可以使用 dict_factory 关键字参数,这正是您需要的 - docs.python.org/3.8/library/dataclasses.html#dataclasses.asdict。使用这种方法,您可以在您的顶级实体上调用asdict,它将转换它及其所有嵌套实体,方便又方便。请在下面使用代码查看我的答案。
    【解决方案2】:

    你可以实现__deepcopy__方法来实现你想要的:

    class FoobarEnum(Enum):
      FIRST = "foobar"
      SECOND = "baz"
    
      def __deepcopy__(self, memo):
          return self.value
    

    asdict 函数处理数据类、元组、列表和字典。如果是它调用的任何其他类型:

    copy.deepcopy(obj)

    不过,像这样覆盖 __deepcopy__ 可能不是最好的主意。

    【讨论】:

    • 它会起作用,但这不是一个好主意。如果您需要在业务逻辑的某个地方创建一个实际的deepcopy,您将拥有一个字符串而不是 Enum。 asdict 有一个关键字参数dict_factory 专门为此docs.python.org/3.8/library/dataclasses.html#dataclasses.asdict 设计。用代码查看我的答案。
    【解决方案3】:

    添加__post_init__,如下所示。

    from dataclasses import dataclass, asdict
    from enum import Enum
    from typing import Union
    
    
    @dataclass
    class Foobar:
        name:str
        template: Union["FoobarEnum", str]
        def __post_init__(self):
            self.template = self.template.value
    
    class FoobarEnum(Enum):
        FIRST = "foobar"
        SECOND = "baz"
    
    foobar = Foobar(name="John", template=FoobarEnum.FIRST)
    print(asdict(foobar))
    

    根据用例,您可以同时继承 strEnum 或拥有一个仅初始化字段。

    【讨论】:

    【解决方案4】:

    你试过了吗?

    import json
    
    def dumps(object):
        def default(o):
            if isinstance(o, Enum):
                # use enum value when JSON deserialize the enum
                return o.__dict__['_value_'] 
            else:
                return o.__dict__
        return json.dumps(object, default=default)
    
    print(json.dumps(YOUR_OBJECT_CONTAINS_ENUMS, default=default))
    

    【讨论】:

    【解决方案5】:
    from dataclasses import dataclass, asdict
    from enum import Enum
    
    
    class FoobarEnum(Enum):
        FIRST = "foobar"
        SECOND = "baz"
    
    
    @dataclass
    class Foobar:
        name: str
        template: FoobarEnum
    
    
    def my_dict(data):
    
        return {
            field: value.value if isinstance(value, Enum) else value
            for field, value in data
        }
    
    
    foobar = Foobar(name="John", template=FoobarEnum.FIRST)
    
    data = {'name': 'John', 'template': 'foobar'}
    
    assert asdict(foobar, dict_factory=my_dict) == data
    

    【讨论】:

    • 对不起。错过了。我正着急写cmets。我昨天想写,但今天才写,所以从技术上讲,你是第一个。我很抱歉。
    【解决方案6】:

    其实你可以做到的。 asdict 有关键字参数dict_factory 允许您在那里处理您的数据:

    from dataclasses import dataclass, asdict
    from enum import Enum
    
    
    @dataclass
    class Foobar:
      name: str
      template: "FoobarEnum"
    
    
    class FoobarEnum(Enum):
      FIRST = "foobar"
      SECOND = "baz"
    
    
    def custom_asdict_factory(data):
    
        def convert_value(obj):
            if isinstance(obj, Enum):
                return obj.value
            return obj
    
        return dict((k, convert_value(v)) for k, v in data)
    
    
    foobar = Foobar(name="John", template=FoobarEnum.FIRST)
    
    print(asdict(foobar, dict_factory=custom_asdict_factory))
    # {'name': 'John', 'template': 'foobar'}
    

    【讨论】:

    • 虽然正确,但这是一种适得其反的处理方式。数据类应该控制它的 asdict() 输出,而不是用户。
    猜你喜欢
    • 1970-01-01
    • 2019-11-12
    • 1970-01-01
    • 2019-10-02
    • 2022-11-03
    • 1970-01-01
    • 2019-03-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多