You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

检查Python导入模块时出现RuntimeError的原因是什么?

为什么你的Python脚本会触发RuntimeError?

这个问题的核心原因是Python 2和Python 3中列表推导式的作用域行为差异,具体来说是Python 2的列表推导式会泄漏循环变量到全局作用域,导致迭代字典时触发了修改。

错误的具体触发流程:

  • 在Python 2.7.12中,列表推导式不会创建独立的局部作用域——当你执行[i.__name__ for i in globals().values() ...]时,循环变量i会被直接添加到globals()字典中。
  • 而你此时正在迭代globals().values(),这个操作要求迭代过程中字典的大小保持不变。突然新增的i变量让globals()的大小发生了变化,Python就会抛出RuntimeError: dictionary changed size during iteration
  • 换到Python 3.6.4运行不会报错,是因为Python 3的列表推导式有自己的局部作用域,循环变量i不会泄漏到全局命名空间,自然不会修改globals()字典。

修复方案:

这里有几个简单的修复方式,既能解决错误,也能让代码更健壮:

  • 提前把globals().values()转为列表:这样迭代的是一个静态副本,不会受原字典变化影响:
    module_list = [i.__name__ for i in list(globals().values()) 
                   if isinstance(i, types.ModuleType) and i in sys.modules.values()]
    
  • isinstance代替type()判断模块类型:这是Python类型检查的最佳实践,能兼容可能的子类情况(虽然模块类型几乎没有子类,但习惯要养好):
    # 替换原来的 type(i) == types.ModuleType
    isinstance(i, types.ModuleType)
    
  • 换一种更高效的遍历方式:直接从sys.modules入手,或者利用globals()的键值对,避免不必要的检查:
    # 直接筛选全局命名空间中的模块,同时确保在sys.modules中存在
    module_list = [name for name, obj in globals().items() 
                   if isinstance(obj, types.ModuleType) and obj in sys.modules.values()]
    

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

火山引擎 最新活动