Python中[]作默认参数会累积数据,因其在函数定义时创建一次并被所有未传参调用共享;正确写法是用None作默认值并在函数内初始化,如def f(lst=None): if lst is None: lst = []。

因为 [] 作为默认参数会在函数定义时被创建一次,之后所有未传参的调用都共享同一个列表对象——这不是“每次调用都新建”,而是“反复往同一个容器里塞数据”,极易引发隐蔽的副作用。
为什么 def f(x=[]) 会累积数据?
Python 函数的默认参数在定义时求值,不是在每次调用时。所以 [] 这个空列表对象只生成一次,后续所有没传 x 的调用,都拿到并修改这个同一对象。
常见错误现象:
- 调用
f()三次,结果返回[1, 1, 1]而不是预期的[1]每次 - 多线程/多请求场景下,不同逻辑意外污染彼此的数据
正确写法:用 None 作默认值再内部初始化
这是最通用、最安全的惯用法,明确把对象创建时机控制在运行时。
立即学习“Python免费学习笔记(深入)”;
def append_item(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst
注意点:
- 必须用
is None判断,不能用if not lst(因为[]是 falsy,会误判传入的空列表) - 如果函数需支持传入
None本身作为有效值,就得换哨兵对象,比如sentinel = object()
哪些类型会踩这个坑?
所有可变对象都存在同样问题,不只是 list:
-
dict:def f(d={})→ 同样累积键值对 -
set:def f(s=set())→ 重复添加元素后去重失效 - 自定义可变类实例,只要没在函数体内显式构造新实例,也会共享
不可变类型(int、str、tuple、None)没有这个问题,因为赋值或运算会生成新对象。
IDE 和 linter 能提前发现吗?
主流工具如 pylint、flake8(配合 pyflakes)和 mypy 都能识别这类模式:
-
pylint报W0102: Dangerous default value [] as argument -
flake8不直接报,但pyflakes插件会提示default-argument-mutable
建议把这类检查加入 CI 流程;但更关键的是形成肌肉记忆——看到可变对象出现在 = 右侧,默认先想“它是不是该挪到函数体里?”


















