Python函数默认参数在定义时求值,可变对象如[]、{}被重复使用导致状态残留;正确做法是用None作默认值并在函数内初始化。

为什么 def func(x=[]): 会“记住”上次调用的修改
Python 函数的默认参数在函数定义时就完成求值,而不是每次调用时重新创建。这意味着 [] 这个空列表对象只在 def 执行那一刻生成一次,后续所有未传参的调用都共享这个同一对象。
常见错误现象:
def append_to(a, lst=[]):
lst.append(a)
return lst
print(append_to(1)) # [1]
print(append_to(2)) # [1, 2] ← 意外!不是 [2]
- 根本原因不是“变量被污染”,而是
lst始终指向同一个list实例 - 该行为对所有可变对象都成立:包括
{}、set()、自定义类实例等 - 不可变对象(如
None、0、"a")没这个问题,因为它们无法被原地修改
正确写法:用 None 作为占位符
标准解法是把默认值设为 None,再在函数体内显式初始化可变对象。
def append_to(a, lst=None):
if lst is None:
lst = []
lst.append(a)
return lst-
is None比== None更安全,且是 Python 社区惯例 - 如果需要预设初始内容(比如默认带一个元素),直接写
lst = [default_item] - 不要用
if not lst:判断——空列表是 falsy,但用户可能真想传入空列表
哪些场景容易踩坑
这类问题高发于构造器、缓存逻辑、递归辅助参数、配置合并等场景。
-
类方法中默认参数用
dict:导致多个实例共享同一字典,互相覆盖键值 -
装饰器里缓存结果:若用
cache={}当默认参数,所有被装饰函数共用一个缓存字典 -
递归函数的 accumulator:比如
def flatten(lst, acc=[]),不同调用链会混在一起 - Flask/Django 视图函数:默认参数若含可变对象,在多请求下状态错乱,极难复现
检查现有代码是否受影响
搜索项目中所有形如 =[]、={}、=set() 的函数参数定义,尤其是非 None 默认值。
本文档主要讲述的是Python之模块学习;python是由一系列的模块组成的,每个模块就是一个py为后缀的文件,同时模块也是一个命名空间,从而避免了变量名称冲突的问题。模块我们就可以理解为lib库,如果需要使用某个模块中的函数或对象,则要导入这个模块才可以使用,除了系统默认的模块(内置函数)不需要导入外。希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
立即学习“Python免费学习笔记(深入)”;
- 静态检查工具(如
pylint)能报dangerous-default-value警告 - 运行时可通过
inspect.signature(func).parameters查看默认值对象 ID 是否变化 - 注意嵌套结构:例如
def f(d={"k": []})同样危险——外层 dict 不可变,但内部 list 是可变的
真正麻烦的不是语法错误,而是它只在特定调用序列下暴露,且表现像“偶发 bug”。一旦默认参数里藏了可变对象,它的生命周期就脱离了你的直觉控制。









