加了 frozen=True 才算真正不可变,因为此时字段赋值会触发 FrozenInstanceError;未设置时字段仍可直接修改,且 hash 不可用。

dataclasses 默认是可变的,要实现不可变且支持比较,必须显式设置 frozen=True,否则 == 比较可能出错,hash() 也用不了。
为什么加了 frozen=True 才算真正不可变?
不设 frozen=True 时,@dataclass 生成的类实例字段仍可被直接赋值修改,比如 obj.x = 10 不报错;而开启后,任何字段赋值都会触发 dataclasses.FrozenInstanceError。注意:这层冻结是运行时检查,不是类型系统层面的约束(mypy 需额外加 unsafe_hash=True 才能通过类型检查)。
-
frozen=True自动启用eq=True(默认已开启),所以字段值相同即==为True - 若需用作字典 key 或集合元素,还必须确保有 hash ——
frozen=True会自动设unsafe_hash=True(除非你手动关掉) - 继承自 frozen dataclass 的子类,也必须显式声明
frozen=True,否则父类冻结失效
field(default_factory=...) 在 frozen 类里怎么写才不报错?
在 frozen=True 下,不能用可变默认值(如 default=[]),但 default_factory 是安全的——因为每次实例化都调用工厂函数,不共享状态。不过要注意:工厂函数本身不能带参数,且返回值必须是不可变对象,否则虽能创建实例,后续比较或 hash 仍可能出问题。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- ✅ 正确:
items: list = field(default_factory=list)—— 实例间互不影响 - ⚠️ 危险:
items: list = field(default_factory=lambda: [1, 2])—— 虽语法合法,但若列表内容被意外修改(比如没意识到它其实是可变的),就会破坏不可变性语义 - ? 更稳妥:用
tuple替代list,或配合typing.Sequence+tuple构造
和 NamedTuple 比,@dataclass(frozen=True) 有什么实际差异?
两者都不可变、可比较、可 hash,但行为细节不同:NamedTuple 字段名是 _fields 元组,不支持默认值(除非用 _replace() 模拟),也不支持类型注解运行时验证;而 @dataclass(frozen=True) 支持全部 PEP 563 特性,字段可带默认值、可选注解、可自定义 __post_init__ 做校验(只要不改字段值)。
- NamedTuple 实例是
tuple子类,支持解包(a, b = obj);dataclass 不支持,除非自己实现__iter__ - dataclass 的
repr更清晰,默认显示字段名;NamedTuple 的repr看起来像函数调用 - 如果用了
__post_init__做合法性检查(例如确保age > 0),必须确保它不给任何字段重新赋值,否则触发 FrozenInstanceError
真正难的不是加 frozen=True,而是确保所有字段类型本身也是不可变的(比如别用 dict 或自定义可变类),以及团队里没人绕过限制用 object.__setattr__(obj, 'x', ...) —— 那就彻底破防了。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










