永远不要用[]、{}或set()作函数默认参数,必须改用None并在函数体内初始化;因为Python在定义时创建并缓存可变默认参数,导致多次调用共享同一对象而引发数据累积、竞态条件和测试污染。

直接说结论:永远不要用 []、{} 或 set() 作函数默认参数;一律改用 None,并在函数体内初始化。
为什么 def func(items=[]) 会出问题
Python 在函数定义时就求值并缓存默认参数,items=[] 这个空列表对象只创建一次,后续所有未传 items 的调用都复用它。不是“每次调用新建一个”,而是“所有人共用一个”。结果就是数据跨调用累积,看似随机,实则必然。
常见错误现象:
- 第一次调用返回
['a'],第二次变成['a', 'b'],第三次是['a', 'b', 'c'] - 多线程或异步环境下,多个协程/线程同时修改同一默认列表,引发竞态或脏数据
- 单元测试里前一个 test 修改了默认列表,导致后一个 test 失败,且难以复现
None + 内部初始化是最可靠写法
这是被 Python 官方文档和所有主流代码规范(PEP 8 衍生实践)明确推荐的方案。它把对象创建时机从“定义时”推迟到“调用时”,彻底切断共享链。
立即学习“Python免费学习笔记(深入)”;
实操建议:
- 把
def add_item(item, items=[]):改成def add_item(item, items=None): - 开头加判断:
if items is None: items = [](注意必须用is None,不用== None) - 如果函数有多个可变默认参数(比如
data_dict和seen_set),每个都单独做is None判断并初始化 - 类方法中同样适用,例如
def append_log(self, msg, log_lines=None):
如何快速发现已有代码里的隐患
别靠肉眼扫,用工具和运行时检查更准。
可用手段:
- 静态检查:启用
pylint的dangerous-default-value规则,或flake8配合pyflakes,能直接标出def f(x=[]):这类模式 - 运行时自查:打印函数的
__defaults__属性,比如func.__defaults__若含[]或{},立刻修正 - 搜索关键词:
=\[\]、=\{\}、=set\(\)(正则),在项目里全局 grep,重点查.py文件中的def行
最易被忽略的是嵌套函数和 lambda 里的默认参数——它们也遵循同一规则,但更难察觉。只要出现可变对象字面量作为默认值,无论在哪一层,都得按 None 方式重写。


















