default_factory必须是无参可调用对象,不能是调用结果;正确写法为list、dict、lambda: []等;不可同时指定default和default_factory;嵌套结构需确保内部对象也独立。

field(default_factory) 必须是可调用对象,不能是调用结果
常见错误是写成 default_factory=list() 或 default_factory={} —— 这会让所有实例共享同一个 list 或 dict。Python 会在类定义时就执行括号,导致默认工厂“提前求值”,失去每次新建实例时重新生成的意义。
正确做法是传函数本身(不带括号):list、dict、lambda: [] 或自定义函数名。dataclasses 在实例化时自动调用它。
-
field(default_factory=list)✅ 每次新建实例都得到新列表 -
field(default_factory=lambda: {"a": 1})✅ 返回新字典,带初始键值 -
field(default_factory=uuid.uuid4)✅ 适合生成唯一 ID -
field(default_factory=datetime.now)✅ 注意:不加括号,否则会冻结为类定义时刻的时间
不能同时指定 default 和 default_factory
两者互斥。如果写了 default=... 又写 default_factory=...,dataclasses 会直接抛出 ValueError: cannot specify both default and default_factory。
典型误用场景:想给一个 list 字段设“默认为空列表”,却写成 field(default=[], default_factory=list)。这既违法,也违背语义——default=[] 本身已触发共享引用问题,应彻底避免。
立即学习“Python免费学习笔记(深入)”;
- 要可变默认值 → 只用
default_factory - 要不可变默认值(如
int、str、None)→ 用default更清晰 -
default=None+ 后续在__post_init__中初始化,也是一种替代方案,但不如default_factory直观
default_factory 函数不能带参数(除非包装)
field 的 default_factory 参数只接受无参可调用对象。如果你需要传参(比如初始化一个带容量的 deque),必须用 lambda 或 functools.partial 封装。
例如,想默认创建 deque(maxlen=5),不能直接写 default_factory=deque(maxlen=5)(会报错且提前执行),而应:
from collections import deque from functools import partial <h1>方式一:lambda(最常用)</h1><p>field(default_factory=lambda: deque(maxlen=5))</p><h1>方式二:partial(更显式,适合复用)</h1><p>field(default_factory=partial(deque, maxlen=5))
注意 partial 返回的是可调用对象,且参数绑定发生在实例化前,不会影响每次调用的独立性。
在 dataclass 初始化后修改 default_factory 返回的对象,不影响其他实例
这是验证 default_factory 是否生效的关键点。只要工厂函数每次返回新对象,各实例的字段就是隔离的。
比如:
@dataclass
class Box:
items: list = field(default_factory=list)
<p>a = Box()
b = Box()
a.items.append("x")
print(b.items) # 输出 [],不是 ["x"]
但如果误用了 default=[],这里就会输出 ["x"] —— 这是 Python 可变默认参数的经典陷阱。用 default_factory 是 dataclasses 提供的官方解法,但它不自动修复旧写法,你得主动改。
最容易被忽略的是嵌套结构:比如 field(default_factory=lambda: [{"id": 0}]) 看似安全,但若后续修改了字典内容(obj.items[0]["name"] = "A"),仍可能引发意外交互——因为字典本身是新对象,但它的内容是否深拷贝,取决于你的工厂函数逻辑。


















