如何将 dict 转换为 dataclass(与 asdict 相反)?

how to convert a dict to a dataclass (reverse of asdict)?

提问人:D.L 提问时间:10/19/2023 最后编辑:petezurichD.L 更新时间:10/20/2023 访问量:316

问:

Dataclasses 模块允许用户方便地从 DataClass Reall 创建字典,如下所示:

from dataclasses import dataclass, asdict

@dataclass
class MyDataClass:
    ''' description of the dataclass '''
    a: int
    b: int
    
# create instance
c = MyDataClass(100, 200)
print(c)

# turn into a dict
d = asdict(c)
print(d)

但是我正在尝试执行相反的过程:dict -> dataclass。

我能做的最好的事情就是将字典解压缩回预定义的数据类中。

# is there a way to convert this dict to a dataclass ?
my_dict = {'a': 100, 'b': 200}
e = MyDataClass(**my_dict)
print(e)

我怎样才能在不必预定义数据类(如果可能)的情况下实现这一点?

蟒蛇 python-dataclasses

评论

0赞 Barmar 10/19/2023
如果不预定义数据类,该类将来自哪里?让它成为一门课有什么意义?
0赞 Jab 10/19/2023
SimpleNamespace 会是你要找的吗?或者也许是一个命名的元组
0赞 Jab 10/19/2023
@Mike'Pomax'Kamermans 数据类在使用提供的语法 OP 时会自动创建构造函数。
0赞 D.L 10/19/2023
@Barmar,我最初是这么想的,但一个类似的(反向)装饰器在逻辑上可以实现这一点......即。应该可以进行方便的反向。
0赞 D.L 10/19/2023
@Jab,这似乎是一个很好的初步建议。

答:

0赞 russhoppa 10/19/2023 #1

我会按照你所展示的去包装字典。

from dataclasses import dataclass, asdict

@dataclass
class MyDataClass:
    a: int
    b: int

c = MyDataClass(100, 200)
print(c)
>>> MyDataClass(a=100, b=200)

d = asdict(c)
print(d)
>>> {'a': 100, 'b': 200}

e = MyDataClass(**d)
print(e)
>>> MyDataClass(a=100, b=200)

不能将字典转换为尚不存在的数据类。在将字典转换为该类之前,必须先创建该类。如果你真的希望有一个类而不是字典,你可以使用枚举。否则,你只能通过捷径来创建类,然后将字典转换为这些类。

评论

0赞 D.L 10/20/2023
这就是我在构建问题时的想法(可能是这种情况),但实际上有 2 个很好的答案可以做到这一点......
0赞 russhoppa 10/20/2023
是的,我喜欢这些答案,但从技术上讲,它们只是“制作课程的捷径,然后将字典变成这些课程”,正如我在回答中所说的那样。
0赞 D.L 10/20/2023
我一直在寻找捷径:)。从逻辑上讲,字典的所有信息(键、值和值的类型)都可以快速映射到数据类中。因此,原生在那里是有道理的......
0赞 russhoppa 10/20/2023
明白了。是的,这是有道理的。
2赞 Seon 10/19/2023 #2

您可以使用make_dataclass

from dataclasses import make_dataclass

my_dict = {"a": 100, "b": 200}
make_dataclass(
    "MyDynamicallyCreatedDataclass", ((k, type(v)) for k, v in my_dict.items())
)(**my_dict)

评论

0赞 D.L 10/19/2023
明。我没有发现这种方法。我总是在这里找到乐于助人的人!!make_dataclass
1赞 Jab 10/20/2023
美丽的答案!并不是说需要任何更改,而只是提供了另一种方法来做到这一点。您可以使用 zip 和 map 代替理解:make_dataclass("MyDynamicallyCreatedDataclass", zip(mydict, map(type, my_dict.values()))
0赞 D.L 10/20/2023
两个很棒的答案(我都没想到)。我将其标记为已接受的答案,因为它更接近解决方案(使数据类)。
1赞 Jab 10/19/2023 #3

可以使用 SimpleNamespacenamedtuple。虽然这不是一个数据类,但它是它自己的对象:

from types import SimpleNamespace
from collections import namedtuple

my_dict = {'a': 100, 'b': 200}

# SimpleNamespace solution
e = SimpleNamespace(**my_dict)

# namedtuple solution
MyClass = namedtuple("MyClass", my_dict)
f = MyClass(**my_dict)

>>> e
namespace(a=100, b=200)
>>> e.a
100
>>> 
>>> MyClass
<class '__main__.MyClass'>
>>> f
MyClass(a=100, b=200)
>>> f.a
100

评论

0赞 D.L 10/19/2023
哇。2个意想不到的优秀答案。这更干净,但解决方案也很好。你有偏好吗?make_dataclass
1赞 Jab 10/20/2023
嗯,这真的要看情况。SimpleNamespace 的作用类似于字典,但允许点表示法。它;本质上是一个对象。您可以随时添加、删除和更改密钥。但实际上make_dataclass构造了一个具有具体字段的新类类型。
1赞 D.L 10/20/2023
我对此投了赞成票,因为它很优雅(并提供了使用点符号的能力)......
0赞 Jab 10/20/2023
@D.L 为了完整起见,我还提供了一个解决方案。命名元组解决方案类似于数据类解决方案,其中类具有具体字段,但在创建后无法修改collections.namedtuple