python默认参数不能使用可变对象,因其在函数定义时创建并被所有调用共享,导致多次调用时累积修改;正确做法是用none作默认值并在函数内新建对象。

Python 中默认参数不能使用可变对象(如 list、dict、set 等),根本原因在于:默认参数在函数定义时被创建并复用,而非每次调用时重新初始化。这导致多次调用函数时,可变默认参数会持续累积修改,产生不符合直觉的副作用。
默认参数只在定义时求值一次
Python 函数的默认参数不是在每次调用时动态生成,而是在 def 语句执行时(即函数定义阶段)计算并绑定到函数对象上。这个对象会被所有后续调用共享。
例如:
def add_item(item, lst=[]):
lst.append(item)
return lst
<p>print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] ← 意外!不是 [2]
print(add_item(3)) # [1, 2, 3] ← 默认列表持续被修改
这里的 lst=[] 在函数定义时就创建了一个空列表对象,并作为函数属性 add_item.__defaults__ 存储。每次不传 lst 时,都复用这个对象。
立即学习“Python免费学习笔记(深入)”;
如何安全地使用“默认可变行为”
正确做法是用不可变占位符(通常是 None),在函数体内按需创建新对象:
- 用
None作为默认值,显式判断后初始化 - 避免直接修改默认参数,而是操作副本(如
lst.copy()或list(lst)) - 若需保留状态(如缓存),应明确设计为有意为之,而非依赖默认参数机制
修正示例:
def add_item(item, lst=None):
if lst is None:
lst = [] # 每次调用都新建
lst.append(item)
return lst
<p>print(add_item(1)) # [1]
print(add_item(2)) # [2]
print(add_item(3)) # [3]
哪些对象属于“可变默认参数陷阱”
所有内置可变类型都适用该规则,包括但不限于:
-
[](list)、{}(dict)、set()(set) - 自定义类的实例(只要其状态可变且未重写相关方法)
-
bytearray、array.array等可变序列类型
注意:str、int、tuple、frozenset 等不可变类型无此问题,因为它们无法原地修改,每次“修改”实际是创建新对象。
面试中常考的延伸点
面试官可能进一步追问:
- 为什么 Python 这么设计? —— 提升定义效率,避免重复构造;符合“默认参数是函数属性”的统一模型
-
如何查看函数的默认参数? ——
func.__defaults__(位置参数)、func.__kwdefaults__(关键字参数) - lambda 能用可变默认参数吗? —— 可以,但同样存在陷阱,原理一致
-
装饰器里怎么避免这个问题? —— 若装饰器内部函数用了可变默认值,也要按
None方式处理









