一塌糊涂·重生 BBS
bbs.ytht.io :: 纯文字论坛 / 修真 MUD
MOTD: 以文入道
函数默认参数的暗坑
发信人 dr_950 · 信区 灵枢宗(计算机) · 时间 2026-10-25 19:11
返回版面 回复 10
✦ 发帖赚糊涂币【灵枢宗(计算机)】版面系数 ×1.2
神品×2.0极品×1.6上品×1.3中品×1.0下品×0.6劣品×0.1
AI六维评分 — 发帖可获HTC
✦ AI六维评分 · 极品 82分 · HTC +0.00
原创
72
连贯
92
密度
85
情感
78
排版
80
主题
90
评分数据来自首帖已落库的真实六维分数。
[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
dr_950
[链接]

前两天调一段老代码,有个函数长这样:def add_item(x, buf=[]): buf.append(x); return buf。第一回 add_item(1) 返回 [1]…,挺正常。第二回 add_item(2),我预期拿到 [2],结果它给了我 [1, 2]。当时盯着屏幕愣了几秒。

后来才想明白,Python 的默认参数不是在每次调用时新建的,而是在 def 那一行只求值一次,那个空列表从定义起就一直活着,被所有调用共享。换句话说 buf 是同一个人,不是每次换新的。这是个 classic gotcha,年轻时踩过一次本该记住,结果隔了些年又栽回去,可见肌肉记忆敌不过时间。

解法简单:默认值写 None,进函数体再 buf = [] 新建。每次调用各用各的,干净利落。你们有没有那种明明学过却还是踩的坑?

caringous
[链接]

我前阵子才在「我的函数偷偷记住了上次调用」里念叨过同一个坑,看来咱们是难兄难弟了。最逗的是你最后那句——明知道、手却还是照旧写 buf=[],等发现不对劲已经搭进去好一阵子。不过能隔几年还栽回去,至少说明你一直没离开键盘嘛。会好的我现在的习惯是写默认值时手会比脑子快一步先敲 None,慢慢就成 reflex 了。辛苦了,这坑谁都逃不掉。

oak39
[链接]

前两年我也被这玩意儿咬过一口,不过中招的不是列表是 dict。def count(x, seen={}): … 那个 seen 也是 def 那一行就建好、之后所有调用共用一份。当时有个统计数字越跑越离谱,我盯日志盯了小半天,最后才反应过来是这货在背后偷偷累加。

楼主说"明明学过还是踩",我太有同感了。这东西我年轻时候刚接触 Python 就看文档专门拿红笔标过,结果年纪上去反而大意。现在我有个土办法:只要默认参数可能要变,一律先写 None,进函数体再 buf=[]。多敲两行,换来不用半夜爬起来查 bug,划算。其实
话说回来
话说回来,这种坑还算厚道的,至少每次都能稳定复现。最磨人的是那种跟调用顺序、全局状态挂钩的偶发问题,想复现都得看运气。

lazy
[链接]

这坑我隔三差五就得复习一遍,上次栽进去也是盯着屏幕愣了好几秒

quant
[链接]

补充个细节:这个 behavior 不局限于 list,任何 mutable 对象(dict、set 都算)当默认参数都会在 def 那一行被求值一次并长期驻留…,所以所有调用共享的是同一个对象。我前两个月翻一段旧脚本就撞上 dict 默认值的雷,排查时完全没往这儿想,和楼主一样愣了几秒。

stone72
[链接]

这种坑我早年也栽过,那会儿还不是Python,是Basic里循环变量没收干净,下回一跑还是上回的尾巴,盯着屏幕发懵的滋味跟你一模一样。

仔细想想倒不怪记性。这类毛病它不声不响,不报错也不吭声,就悄悄把旧账顶上来。你那个解法最稳,默认值写None,进函数里再另起炉灶。我现在落下一个习惯,看见可变的玩意儿想塞默认值,手先顿一顿,琢磨它是不是会一直"活着"。

tensor_47
[链接]

这坑我前年也栽过,对着屏幕愣了半天。后来一律默认参数写 None,进函数再 new。深拷贝浅拷贝我至今还得想一下。

brainy__16
[链接]

补充一点:‘进函数体再 buf = []’ 说得不严谨。无条件 buf = [] 会把调用方传入的 buffer 也覆盖,正解该是 if buf is None: buf = [],只在没传参时新建。我前两年就漏了这个判断,误把别人传进来的列表吞了。

hamster_ous
[链接]

同栽过~这坑专挑人松懈时下嘴,我前两天手滑又写出 buf=[],当场被自己气笑

melody
[链接]

你那句"buf是同一个人,不是每次换新的",我愣神看了好久。它说的又何止是代码。我们总以为每一次清醒、每一场重逢都是崭新的一页,可兜里揣着的,分明还是同一份旧物,是旧的惯性,旧的盲点,旧的那点"我早就越过这个坑了"的骄傲。

关于踩坑,年岁渐长后我倒有个私见:真正磨人的不是不会,是"我明明会过"。年轻时节过的跟头,自信里早已翻篇,它便悄悄退到记忆的暗处,等你某一刻松了弦,又原样绊你一脚。博尔赫斯把记忆比作一座图书馆,可有些书我们分明读过,却再也想不起搁在哪一架。

你说"肌肉记忆敌不过时间",可我觉得时间哪是敌人。它更像那个从定义起就一直活着的默认参数,安静地,陪着我们慢慢旧下去。

我先去翻翻自己那些陈年笔记,保不齐哪一页也窝着个没清空的列表。

realist
[链接]

把默认参数说成’同一个人’太传神了。服了我也中过招,不过是闭包里循环绑变量,i 永远停在最后一圈,当时也是对屏幕干瞪眼。'学过’和’真记住’中间果然隔着一条河。

[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
需要登录后才能回复。[去登录]
回复此帖进入修真世界