You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Python中列表推导式创建4个协程仅触发1条RuntimeWarning,多次直接赋值却触发4条?

为何Python中列表推导式创建4个协程仅触发1条RuntimeWarning,多次直接赋值却触发4条?

这是个非常有意思的细节问题,本质和Python的垃圾回收时机、协程对象的警告触发逻辑,以及CPython警告系统的去重规则密切相关,咱们一步步拆解:

1. 先搞懂警告的触发逻辑

RuntimeWarning的触发时机是未被await的协程对象被垃圾回收时——协程对象的__del__方法会检查自身是否被正确await过,若没有则抛出该警告。

2. 两种创建方式的核心差异

情况1:列表推导式创建4个协程(仅触发1次警告)

coros = [some_async_func() for _ in range(4)]

这4个协程对象都被coros列表持有引用,所以在main函数执行过程中,它们不会被垃圾回收。只有当main函数执行完毕,coros变量离开作用域,整个列表被回收时,里面的4个协程才会被批量销毁。

而CPython的警告系统默认会对同一代码位置产生的相同警告进行去重——这4个协程都来自同一行代码(列表推导式的那一行),所以它们的警告被视为“同一来源”,最终只输出1次。

情况2:4次单独赋值协程(触发4次警告)

z = some_async_func()
z = some_async_func()
z = some_async_func()
z = some_async_func()

每次给z赋值新的协程对象时,前一个z引用的旧协程就失去了所有引用,会被Python的垃圾回收器即时回收(小对象的快速回收机制)。更关键的是:每个协程都来自不同的代码行,所以它们的警告被视为“不同来源”,不会被去重,最终输出4次警告。

3. 同时打开两个例子为何只触发4次警告?

当你同时启用两段代码时:

  • 4次单独赋值的协程中,前3个会在被覆盖时即时回收,触发3次警告;第4个协程会和coros列表一起,在main函数结束后才被回收。
  • 而当程序即将退出时,Python会进入收尾清理阶段,此时CPython可能会跳过部分对象的__del__方法执行(或警告系统已停止工作),导致coros列表里的4个协程以及第4个单独赋值的协程中,只有1次警告被输出(或者都被抑制)。最终总警告数和单独启用第二个例子时一致,为4次。

补充验证小实验

如果你修改第一个例子,把列表推导式拆成4行手动添加协程:

coros = []
coros.append(some_async_func())  # 行1
coros.append(some_async_func())  # 行2
coros.append(some_async_func())  # 行3
coros.append(some_async_func())  # 行4

这时候每个协程来自不同代码行,单独启用这个例子会触发4次警告,和第二个例子的行为一致——这也印证了“代码位置是警告去重的关键因素”。

python version: 3.12.8

备注:内容来源于stack exchange,提问作者Kitaram

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 12:24:50