python默认参数的“危险”源于可变对象在函数定义时仅创建一次并被多次调用共享。正确做法是用none作默认值并在函数内初始化,如items=none后检查并赋值空列表。

Python默认参数的“危险”并非语言缺陷,而是对可变对象作为默认值时行为的理解偏差。核心问题在于:函数定义时默认参数只被创建一次,后续调用若修改了该对象,变化会累积保留。
可变默认参数的陷阱本质
Python中,默认参数在函数定义时求值并绑定,而非每次调用时新建。如果默认值是列表、字典、集合等可变对象,它就成了所有调用共享的“同一个对象”。
- 第一次调用函数未传参 → 使用默认列表,往里append一个元素
- 第二次调用同样没传参 → 还是那个列表,再append → 元素变两个
- 第三次……以此类推,列表持续增长
典型错误写法与后果
下面这个例子最常被用来演示问题:
def add_item(item, items=[]):
items.append(item)
return items
<p>print(add_item('a')) # ['a']
print(add_item('b')) # ['a', 'b'] ← 意外!
print(add_item('c')) # ['a', 'b', 'c'] ← 更意外!
你以为每次都是新列表,实际用的是同一个[]实例。
立即学习“Python免费学习笔记(深入)”;
安全的替代写法
正确做法是用None作默认值,在函数体内显式创建新对象:
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
<p>print(add_item('a')) # ['a']
print(add_item('b')) # ['b']
print(add_item('c')) # ['c']
- 所有调用互不影响,符合直觉
- None是不可变单例,不会被意外修改
- 这种模式是Python社区广泛接受的惯用法
哪些类型容易踩坑?哪些相对安全?
危险类型(可变对象):list、dict、set、bytearray、自定义类实例;
安全类型(不可变对象):None、int、float、str、tuple、frozenset。
注意:即使字符串和元组不可变,也不建议用它们做“逻辑上应每次新建”的默认值(比如默认消息模板),虽不报错但语义易混淆,统一用None更清晰。










