检查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




