Python:检查导入的模块时,此RuntimeError的来源是什么?

Python: What is the source of this RuntimeError when checking imported modules?

我想做的是:

检查我在脚本(python 2或3)中导入的不同模块,并打印它们的名称。我目前使用的是python 2.7.12和3.6.4。

我开始的地方:

脚本只需导入模块,使用列表理解功能查找其中的内容,并打印列表:

1
2
3
4
5
6
7
8
9
#!/usr/bin/env python

import sys
import os
import types

module_list = [i.__name__ for i in globals().values()
               if type(i) == types.ModuleType and i in sys.modules.values()]
print(module_list)

我最初是在python 3中运行这个程序的,它工作得很好(对于python 2也很好)。输出为:

1
2
['__builtin__', 'sys', 'os', 'types']  # Python 2.
['builtins', 'sys', 'os', 'types']     # Python 3.

此时,我决定扩展列表理解,因为我发现这是破译代码/逻辑流的良好实践。因此,我用以下内容替换了单个语句:

1
2
3
4
module_list = []
for i in globals().values():
    if type(i) == types.ModuleType and i in sys.modules.values():
        module_list.append(i.__name__)

这就是事情变得多毛的地方。我得到一个RuntimeError,现在输出是:

1
2
3
...
    for i in globals().values():
RuntimeError: dictionary changed size during iteration

但是,它仍然可以在Python2中工作。我现在比任何事情都好奇,尝试了一些事情。

我的尝试:

  • globals().values()变成一个列表:

    1
    2
    3
    for i in list(globals().values()):
        if type(i) == types.ModuleType and i in sys.modules.values():
            module_list.append(i.__name__)

    这与Python2或3中的预期效果相同。

  • 移除list()并添加适当的main结构:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def main():
        module_list = []
        for i in globals().values():
            if type(i) == type(sys) and i in sys.modules.values():
                module_list.append(i.__name__)
        print(module_list)

    if __name__ == '__main__':
        main()

    这与Python2和3中的预期效果相同。

回顾一下:

  • 清单理解适用于python 2和3。
  • for循环仅适用于python 2。
  • 封装在一个list中可以为python 2和3工作。
  • 适当的main结构适用于python 2和3。

问题是:

python 3中的for循环破坏了什么?


在获取values迭代器后,将for i in globals().values():i添加到全局命名空间。如果你把i = None放在循环之前,或者把for i in list(globals().values()):放在循环之前,它就会工作。

在python 2中,values()返回一个值列表,但在python 3中,它是一个迭代器。该迭代器检测到更改并引发错误。