dataclass 默认生成 __init__、__repr__、__eq__,启用 order=true 时追加比较方法,但不生成 __hash__;需 frozen=true 或手动定义才能支持哈希;可变默认值须用 default_factory;frozen=true 禁止所有属性修改;嵌套 dataclass 转 json 应先 asdict 再处理。

dataclass 会自动帮你生成哪些方法?
它默认生成 __init__、__repr__、__eq__,如果启用了 order=True 还会加 __lt__ 等比较方法。但不会生成 __hash__ —— 即使你希望实例能放进 set 或当字典键,也得显式设 frozen=True 或手动定义 __hash__。
常见错误现象:TypeError: unhashable type: 'MyData',往往就是忘了 frozen=True 或没关掉 unsafe_hash=False(默认值)。
使用场景:
- 需要做值比较(比如缓存 key、去重)→ 开
eq=True(默认) +frozen=True - 需要排序 → 加
order=True,但注意字段类型必须支持比较(str、int可以,list默认不行) - 调试时想快速看结构 →
repr=True(默认),但敏感字段可用repr=False隐藏
可变默认值必须用 field(default_factory=...)
直接写 tags: list = [] 是危险的:所有实例共享同一个空列表。Python 解释器在类定义时就求值了这个默认值,不是每次实例化才创建。
正确做法是:
- 用
field(default_factory=list)→ 每次新建实例都调用list() - 自定义工厂函数也行:
field(default_factory=lambda: [1, 2, 3]) - 不可变类型(
str、int、None)可以直写默认值,没问题
性能影响:如果 default_factory 里做了耗时操作(比如读文件、发 HTTP 请求),会拖慢实例化速度——这类逻辑应移出初始化过程。
frozen=True 后还能改属性吗?
不能。一旦启用,任何赋值都会抛 dataclasses.FrozenInstanceError。这不是“只读属性”,而是彻底禁止写入,连 self.__dict__ 直接改都不行。
容易踩的坑:
- 继承链中父类用了
frozen=True,子类即使没写也会继承冻结行为 - 想临时绕过?别试
object.__setattr__(obj, 'x', val)—— 它在frozen=True下也被拦截 - 需要部分字段可变?只能不用
frozen,靠文档或类型提示约束
嵌套 dataclass 怎么转 JSON?
asdict() 能递归展开嵌套的 @dataclass 实例,但对 datetime、Decimal 等非基础类型仍会失败。
实操建议:
- 先用
asdict(obj)转成纯字典 - 再传给
json.dumps(..., default=str)—— 把无法序列化的对象统一转字符串(适合调试) - 生产环境推荐用
dataclasses.asdict()+ 自定义default函数,例如对datetime返回 ISO 格式字符串 - 别依赖
json.dumps(obj)直接序列化 dataclass 实例 —— 它不认识你的类
最常被忽略的一点:frozen=True 和 slots=True 一起用才能真正节省内存;单独开 frozen 只防修改,不省空间。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











