python中def func(x=[])的列表会“记住”上次值,是因为默认参数在函数定义时求值一次并复用同一对象;安全做法是用none占位并在函数内初始化。

为什么 def func(x=[]) 的列表会“记住”上次调用的值
根本不是 Python “记性好”,而是默认参数在函数定义时就完成了求值,且只执行一次。这个 [] 对象在 def 语句执行时被创建,绑定到函数对象的 __defaults__ 元组里,后续所有未传参的调用都复用同一个列表实例。
常见错误现象:func() 第一次返回 [1],第二次返回 [1, 1],第三次变成 [1, 1, 1]——这不是 bug,是对象复用的必然结果。
- 使用场景:常出现在缓存、累积日志、构建递归初始容器等意图中,但多数人没意识到自己在共享可变对象
- 参数差异:不可变默认参数(如
None、0、"a")完全安全;可变类型(list、dict、set)才危险 - 性能影响:看似省了初始化开销,实则埋下隐蔽状态依赖,调试成本远高于那点内存分配
怎么安全地实现“每次调用都拿到新列表”
标准解法是用 None 占位,再在函数体内做惰性初始化。这是 Python 社区约定俗成的写法,解释器和人一眼都能看懂意图。
示例:
立即学习“Python免费学习笔记(深入)”;
def append_to(items=None):
if items is None:
items = []
items.append("new")
return items
- 绝对不要写
if not items:—— 空列表是 falsy,会导致误判 - 不要用
items = items or []—— 同样会把[]当成 false,重蹈覆辙 - 如果真需要区分
None和空容器,就用哨兵对象:_sentinel = object(),然后判断items is _sentinel
__defaults__ 能直接改,但千万别碰
你可以读写函数的 __defaults__,比如 func.__defaults__ = (["hacked"],),但这属于元编程黑魔法,和修改内置类型一样危险。
传媒企业网站系统使用热腾CMS(RTCMS),根据网站板块定制的栏目,如果修改栏目,需要修改模板相应的标签。站点内容均可在后台网站基本设置中添加。全站可生成HTML,安装默认动态浏览。并可以独立设置SEO标题、关键字、描述信息。源码包中带有少量测试数据,安装时可选择演示安装或全新安装。如果全新安装,后台内容充实后,首页才能完全显示出来。(全新安装后可以删除演示数据用到的图片,目录在https://
实际后果:
- 多个线程同时调用该函数时,可能看到不一致的默认值
- 被装饰器包装后,
__defaults__可能指向代理对象,直接赋值会静默失败 - PyPy 或某些静态分析工具(如 mypy)可能无法正确推导类型
一句话:它存在,是为了让你理解机制,不是为了让你修改。
类方法里用可变默认参数更隐蔽
类定义体内的函数(包括 @staticmethod、@classmethod)同样受此规则约束。尤其容易踩坑的是带默认参数的 __init__ 或工厂方法。
示例:
立即学习“Python免费学习笔记(深入)”;
class Box:
def __init__(self, contents=[]): # 危险!
self.contents = contents
- 所有未传
contents的Box()实例,共享同一个contents列表 - 即使你给实例赋了新值(
box.contents = ["a"]),也只覆盖了实例属性,不影响其他实例的引用 - 比普通函数更难发现,因为出问题时往往表现为“两个毫不相关的对象内容突然同步了”
真正复杂的地方从来不在语法层面,而在于你是否意识到:那个看似无害的方括号,在函数诞生那一刻,就已经悄悄活了过来。









