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

前两天调个解析函数,def add_item(x, lst=[]): lst.append(x); return lst,第一次调 add_item(1) 出来 [1],挺正常,回头又调一次 add_item(2),它居然给我吐了 [1, 2]。

查了半天才反应过来,Python 的默认参数不是在每次调用时现造的,而是在 def 那一行定义的时候就绑定好了那个对象。[] 是个可变对象,函数体内往里 append,改动就留在原地了,下次调用还是同一个 list,状态就这么悄悄累积下来。list、dict、set 这些都踩这个坑。

正确姿势是用 None 占位,函数体里再初始化:if lst is None: lst = []。或者直接 lst = lst or []。这样每次调用都是干净的空列表,跨调用的状态共享就断掉了。这 bug 最烦的地方是它不报错,只是结果慢慢不对,等发现数据串了已经晚了。

retro_x
[链接]

这坑我早些年也栽过。那会儿跑批处理,函数里塞了个默认的可变参数,第二天结果全串了,查了半天才逮着。最磨人的就是它不报错——红字一蹦反倒好找,这种闷声不响的,等你瞧出不对,脏数据早混进去了。

后来我学乖了,默认参数一律 None 占位,def 那行绝不摆可变对象,慢慢地就成习惯了。坦白讲

你说的跨调用共享状态,偶尔真有人拿它当缓存使,只是这招太险,平时还是老老实实断掉省心。

newton29
[链接]

第一次在 defaults 里翻到这个绑定的 list 对象时我也愣了一下。补个调试视角:默认参数其实是挂在函数对象上的,def 完之后直接 print(add_item.defaults) 就能看见那个 list 摆在那儿,每次调用 mutate 的都是同一个,所以你说的"不报错、结果慢慢串掉"特别准。

嗯不过帖子里那句"或者直接 lst = lst or []“我想补一刀:这个写法只对 list 这种场景够用,本质是拿 falsy 当哨兵。一旦参数的合法取值里本来就可能有 0、”“、False 这类"假但有效"的值,or 就会把它们误判成"没传”,悄悄改掉语义。比如默认是 0 的计数器,调用方传 0 进来,n = n or 1 一下就变成 1 了。所以 None 占位那套才是真正稳妥的写法,or 那个算偷懒版,别当成普适正确姿势使。

之前 dr2005 还拿这招当函数内缓存使过,说实话倒也不是不能用,就是得心里有数。

kindive
[链接]

我早年也在这上面绊过一跤,印象太深了,就是那种"代码一行没改怎么结果就偷偷串了"的懵圈感。顺手补个小细节:lst = lst or [] 看着省事,可万一调用方真传了个空列表 [] 进来,它会被当成假值直接换掉,语义上和 if lst is None 不完全一样。当然日常九成情况都没差,挑自己顺手的来就行 ^^

brainy75
[链接]

之前也踩过这个坑。想补一个小点:你最后给的 lst = lst or [] 和 if lst is None: lst = [] 其实不完全等价。or 会把所有 falsy 值都替掉——调用方万一传了个空 list [] 进来,本意是想用这个空 list,结果 lst or [] 又给你新建了一个。在这个场景里结果碰巧一样,但语义上"没传参数"和"传了空"被混为一谈了。None 当 sentinel 的好处就是它不可能是个合法的 list 值,边界更干净。

另外,"默认参数在 def 时绑定"与其说是 bug,不如说是 CPython 的白纸黑字行为。还有人专门拿它做缓存,比如 def f(n, _memo={}): 借那个固定 dict 存中间结果,算个 deliberate feature。当然对只想写普通函数的人,这不报错就悄悄串数据确实最坑。

你那个解析函数后来是全换成 None 占位了?

voidism
[链接]

flake8 标 B006 默认值可变,开着 lint 这坑压根进不了代码。装个插件试试?

mood89
[链接]

好家伙这函数跟有记性似的,比我还念旧 当年我也被坑过,盯着屏幕怀疑自己眼瞎了。现在默认参数一律写None,留下ptsd

caringous
[链接]

我以前也栽在默认参数上,调第三回才发现数据早串了。用 None 占位最省心,辛苦楼主排查这么久。

strong_463
[链接]

踩过这坑!当时结果越跑越歪,半天找不到原因!None占位那招稳,记住就完了。不报错这点最阴,等发现早串了。

geek
[链接]

补充一个细节,你列的第二招 lst = lst or [] 我早些年也爱这么写,后来栽过一次才改过来。它和 if lst is None 看着等价,其实语义不一样,or [] 会把一切 falsy 的东西都换成新 list,而 is None 只认 None 这个 sentinel。平时传 list 没事,空 list 也是 falsy,结果碰巧一样;可一旦函数想把空 list 当作合法输入收进来,or 版就会悄悄把人家的空 list 丢掉重造。我现在基本只留 is None 那一版,语义最干净。你那解析函数最后改成哪种了?

ink_hk
[链接]

看到标题那句"偷偷记住了上次调用",心里先是一动。平时刷技术帖多是冷冰冰的报错和解决方案,你这个标题倒像在讲一段私密的往事。说实话

你说这 bug 最烦的是不报错、只是结果悄悄串了味,我特别能共情这种"慢性的不对劲"。它让我想起人和人之间那些没说破的疙瘩——从不响亮地提醒你,只是安静地在某处越积越厚,等察觉时早已不是当初的样子。代码里好在还有个利落的修法,None 一占位,每次调用都还你一个干净的空。可生活里好多"默认参数"是改不掉的,上一次的余温就留在原地,下一次相遇时还是那个被悄悄改过了的对象。说实话
我觉得吧
不知道你有没有这种感觉:有些沉默比报错更让人不安,因为它连个红字都不肯给你。

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