You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用冻结dataclass作为字典键实现YAML的序列化与反序列化?

问题原因与解决方案

你的推测是对的,问题确实出在冻结dataclass的构造逻辑上。

PyYAML默认构造Python对象时,会先创建一个空的类实例,再尝试给实例的属性赋值。但冻结dataclass(frozen=True)创建后不允许修改属性,更关键的是:空实例没有初始化name字段,而冻结dataclass的__hash__方法是基于所有字段自动生成的,当把这个空实例作为字典键计算哈希时,就会因为找不到name属性抛出AttributeError。

不放弃冻结特性的解决方法

方法一:自定义YAML构造器

通过给Person类注册自定义的YAML构造器,让PyYAML直接用构造函数传入字段值初始化实例,跳过“空实例赋值”的步骤:

from dataclasses import dataclass
import yaml

@dataclass(frozen=True)
class Person:
    name: str

# 自定义构造器:解析YAML数据后直接调用Person构造函数
def construct_person(loader, node):
    data = loader.construct_mapping(node)
    return Person(**data)

# 将构造器与Person类的YAML标签关联
yaml.add_constructor('!!python/object:__main__.Person', construct_person)

# 测试代码
p = Person("Jane")
d = {p: "Hi!"}

with open("data_t.yaml", "w") as f:
    yaml.dump(d, f)

with open("data_t.yaml", "r") as f:
    d2 = yaml.load(f, Loader=yaml.Loader)

print(d2)  # 输出:{Person(name='Jane'): 'Hi!'}

方法二:实现__getstate__和__setstate__

通过定义这两个方法,让PyYAML在反序列化时正确初始化冻结实例的属性:

from dataclasses import dataclass
import yaml

@dataclass(frozen=True)
class Person:
    name: str

    def __getstate__(self):
        # 序列化时返回字段数据
        return {"name": self.name}

    def __setstate__(self, state):
        # 反序列化时,用object.__setattr__绕过冻结限制初始化属性
        object.__setattr__(self, "name", state["name"])

# 测试代码
p = Person("Jane")
d = {p: "Hi!"}

with open("data_t.yaml", "w") as f:
    yaml.dump(d, f)

with open("data_t.yaml", "r") as f:
    d2 = yaml.load(f, Loader=yaml.Loader)

print(d2)  # 输出:{Person(name='Jane'): 'Hi!'}

两种方法都能保留frozen=True的特性,解决YAML反序列化时的报错问题。

内容的提问来源于stack exchange,提问作者user936580

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 07:00:59