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

Python 3.5中list(globals().items())引发RuntimeError问题咨询

关于list(globals().items())触发RuntimeError的原因分析

这问题确实有点 tricky,我来帮你梳理下可能的原因:

直接触发点:迭代字典时被修改

RuntimeError: dictionary changed size during iteration 这个错误的核心逻辑很明确:当你迭代一个字典(这里是globals().items()返回的迭代器)的过程中,字典的大小(键的数量)发生了变化——要么新增了键,要么删除了键,Python就会抛出这个错误来避免迭代结果不一致。

你可能会疑惑:我用list()把迭代器转成列表,不是应该一次性完成吗?其实不是,list()的本质是遍历迭代器并逐个添加元素,这个遍历过程不是原子操作,中间是有间隙的。

GIL的认知误区:它不保护整个操作的原子性

你提到以为list()调用受GIL保护,这是一个常见的误解:

  • GIL确实保证同一时间只有一个Python线程在执行字节码,但它会在每执行一定数量的字节码(或者遇到I/O操作)时释放,让其他线程有机会执行。
  • list(globals().items())这个操作拆分成多个字节码步骤:获取globals()字典、调用items()生成迭代器、遍历迭代器填充列表。每两个字节码执行之间,GIL都可能被释放,这时候其他线程(甚至是主线程里的信号处理函数)就有机会修改globals()字典。

哪怕你没有显式开启多线程,信号处理函数也是一个很容易被忽略的“并发”场景:比如你的程序收到SIGINT(Ctrl+C)或者系统发送的其他信号,信号处理函数如果修改了globals()里的内容,刚好在list()遍历的间隙触发,就会导致这个错误。

结合你提到的lib2to3 token.py代码

你提到的lib2to3/pgen2/token.py第73行(Python3.5版本)是一行__file__ = __file__的代码,这行本身不会修改globals的大小,但如果这段代码是在模块加载过程中执行的,而你的list(globals().items())刚好在模块导入的间隙运行,就可能遇到其他导入操作修改globals的情况:

  • 模块导入时,Python会自动把模块名、导入的对象等添加到当前globals字典中,如果这个导入操作刚好发生在list()遍历迭代器的过程中,就会触发字典大小变化的错误。

为什么无法复现?

这种问题属于竞态条件,只有当修改globals的操作刚好卡在list()遍历的间隙时才会触发,概率很低。你能重现两次已经算幸运了,之后碰不到完全正常——毕竟要刚好卡到那个时间窗口太难了。

可能的验证方向

如果想进一步确认,可以尝试:

  • 检查代码中是否有注册信号处理函数,且处理函数里有修改globals的逻辑;
  • 查看触发错误的上下文,是否有隐式的模块导入(比如在函数里动态导入模块);
  • 尝试在循环中反复执行list(globals().items()),同时给程序发送信号,大概率能复现这个错误。

内容的提问来源于stack exchange,提问作者Dmitry Moroz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 06:44:09