Python 默认参数:为什么列表会记住上一次调用?
函数第一次返回一个主题列表,第二次调用时却带上了第一次的内容。排查这种现象时,需要检查列表是在函数体内创建,还是写在参数默认值里。
1. 两次调用共用了一份列表
下面使用完全独立构造的数据,不读取文件或业务信息:
def add_topic_bad(topic, topics=[]):
topics.append(topic)
return topics
first = add_topic_bad("python")
second = add_topic_bad("csv")
print(first is second)
print(first)
输出:
True
['python', 'csv']
这里不仅是两个结果内容相同。first is second 为 True,说明它们引用同一个列表。第二次 append 也改变了 first 仍然引用的对象。
Python 在执行函数定义时求值默认参数表达式,不会在每次省略参数的调用中重新执行 topics=[]。因此,该函数定义产生的默认列表被后续省略此参数的调用复用。这项规则在 Python 官方教程与编程 FAQ 中都有说明。
2. 在需要时创建新列表
若接口约定“未提供列表时,每次创建一份”,可以使用 None 表达这个条件:
def add_topic(topic, topics=None):
if topics is None:
topics = []
topics.append(topic)
return topics
left = add_topic("python")
right = add_topic("csv")
print(left is right)
print(left, right)
provided = []
returned = add_topic("git", provided)
print(returned is provided, provided)
输出:
False
['python'] ['csv']
True ['git']
前两次调用分别走入初始化分支,得到独立列表。最后一次显式传入 provided,函数仍在调用者的列表上追加数据,返回的也是同一对象。
3. 不要把两种接口约定混在一起
is None 只判断是否使用约定的缺省标记。如果改写为 topics = topics or [],空列表也会进入替换分支;调用者传入的空列表不再是被修改和返回的那一个对象。若接口承诺原地追加,这样的简写就改变了行为。
同样,使用 None 初始化只消除了省略参数时的意外共享,并没有承诺“不修改调用者传入的列表”。如果需要后一种行为,应另行设计复制策略,并明确嵌套对象的处理方式。
当 None 本身也是合法业务数据时,还需要独立的缺省标记。本例将 None 明确定义为“创建新列表”,没有覆盖那种接口。
4. 验证范围
本次在本机 Python 3.9.6 中直接执行上述两段代码,打印结果与文中输出一致;十项断言覆盖默认列表共享、两次缺省调用独立、显式空列表原地修改、非空列表保留已有元素,以及显式传入 None 时重新创建列表。样例只覆盖普通列表、字符串和 None,不涉及并发、缓存或自定义容器。
官方参考:
- Python 编程 FAQ:
docs.python.org/3/faq/programming.html#why-are-default-values-shared-between-objects - Python 教程默认参数:
docs.python.org/3/tutorial/controlflow.html#default-argument-values
本文由 AI 辅助整理,示例数据为独立构造,并已在本机执行自检。
- 点赞
- 收藏
- 关注作者
评论(0)