我有一个数据类,它的字段template
类型为Enum。当使用asdict
函数时,它会将我的数据类转换为字典。是否可以使用FoobarEnum
的value
属性来返回字符串值,而不是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))
根据用例的不同,您可以从str
和Enum
继承,也可以有一个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))