如何在dataclasses模块的asdict函数中使用枚举值



我有一个数据类,它的字段template类型为Enum。当使用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'}

实际上你可以做到。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'}
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

我遇到了一个类似的问题,需要将我的数据类对象序列化为JSON,并通过添加str作为FoobarEnum继承自的第一个类来解决这个问题:

import json
from dataclasses import dataclass, asdict
from enum import Enum

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

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

foobar = Foobar(name="John", template=FoobarEnum.FIRST)
print(json.dumps(asdict(foobar)))

它不会改变asdict的行为,但我现在可以序列化对象了。

参考:将Enum成员序列化为JSON

这是用标准库无法完成的,除非是我不知道的元类枚举破解。CCD_ 9和CCD_。

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

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

如果您想将Enum成员(而不仅仅是Enum.value(保留为数据类成员,并让一个函数将其转换为返回Enum.vvalue而不是Enum成员的dictionary,那么正确的方法是实现自己的方法,将数据类作为dictionary返回。

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'}

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

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

asdict函数处理数据类、元组、列表和dict。在任何其他类型的情况下,它调用:

copy.deepcopy(obj)

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

添加__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继承,也可以有一个init only字段。

你试过这个吗?

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))

最新更新