结论:可变对象(如[]、{})作默认参数会在函数或类定义时创建一次并被所有调用共享,导致数据污染;正确做法是用None占位并在函数体内初始化。

直接说结论:因为类定义时就创建了那个可变对象,并把它绑在类属性或函数默认值上,所有实例或调用共享同一内存地址。
类方法参数用 [] 或 {} 会污染数据
这是最常被忽略的场景。比如写一个工具方法:
class Processor:
def process(self, items=[]):
items.append("processed")
return items
第一次调用 Processor().process() 返回 ["processed"];第二次调用又返回 ["processed", "processed"]——不是因为实例状态,而是因为 items 默认值在类定义时就生成了一个列表,之后所有未传参的调用都复用它。
- 根本原因:
process.__defaults__里存的是那个已修改过的列表对象 - 验证方式:
print(Processor.process.__defaults__)能看到累积后的结果 - 影响范围:不限于
__init__,任何带默认值的方法都中招
@dataclass 中直接写 field: list = [] 是共享的
dataclass 不是“语法糖”那么简单,字段默认值在类定义阶段就被求值。下面这段代码看似无害:
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
立即学习“Python免费学习笔记(深入)”;
from dataclasses import dataclass
@dataclass
class Config:
tags: list = []
但 Config().tags.append("debug") 后,所有新创建的 Config() 实例的 tags 都会包含 "debug"。
- 错误本质:空列表
[]是可变对象,在@dataclass解析类时只创建一次 - 正确写法必须用
field(default_factory=list) - 注意:
default_factory必须是可调用对象,不能写成default_factory=[]
为什么 None 就安全,而 [] 不安全
关键不在“有没有默认值”,而在“这个值是否会被多次修改”。None 是不可变单例,每次进函数都只是做一次 is None 判断,然后自己新建列表;而 [] 一进来就是个活的对象,后续所有操作都在改它。
-
int、str、tuple作默认值没问题,因为它们不可变 -
list、dict、set、自定义可变类实例,全部危险 - 连
datetime.now()这种表达式都不能当默认值——它虽不可变,但只求值一次,永远卡在定义那一刻
真正容易被忽略的点是:这种污染不报错、不抛异常,只在逻辑结果上悄悄偏移。查 bug 时容易盯死运行时数据流,却忘了去看函数定义那一行——那里才是问题的出生地。

















