Python: globals().items() 迭代尝试改变字典

Python: globals().items() iterations try to change a dict

cso.data.__init__.py 中,我正在尝试执行以下操作:

from  cso.data._features  import * # import sumbodule 

for k, v in globals().items():
    if isinstance(v, entity):# entity is some type
        print(k,v)

令我惊讶的是,我得到:

Traceback (most recent call last):
  File "X:\Programming\workspaceEclipse\PyCommonSence\src\cso\data\__init__.py", line 20, in <module>
    from  cso.data._features  import *
  File "X:\Programming\workspaceEclipse\PyCommonSence\src\cso\data\__init__.py", line 49, in <module>
    for k, v in globals().items():
RuntimeError: dictionary changed size during iteration

出于某种原因,它试图以某种方式修改迭代的全局变量。怎么可能 ? 是否有另一种更好的方法来列出包中所有模块的 name: value 形式的所有变量?

列表理解等价物没有错误,但打印了两次:

print([ (k, v) for k, v in globals().items() if isinstance(v, entity)])

这里发生了什么?

因为 for 循环语句创建了一个新变量,在您的特定情况下:

for k, v in globals().items():
    ...

您已经在全局命名空间中创建了 kv字面意思是 dict

另一方面,列表推导式创建了自己的局部函数作用域。只要做:

for k, v in globals().copy().items():
    ...

如果您不想复制,则执行:

k,v = None, None
for k,v in globals().items():
    ...

或者更好的是,将所有这些都包装在一个函数中,这样您就不会不小心触及全局命名空间

当您 运行 在全局范围内编写代码时,您分配的名称也是全局的。 kv 首先分配 开始迭代 globals().items() 之后,这会在中添加 'k''v' 的条目globals() 字典。

您可以通过在迭代之前显式复制 dict 来避免这种情况,例如:

for k, v in globals().copy().items():

或者通过在函数中完成工作(它将在局部范围内存储迭代变量):

def print_global_entities():
    for k, v in globals().items():
        if isinstance(v, entity):# entity is some type
            print(k,v)

if __name__ == '__main__':
    print_global_entities()

这样做实际上可以让您的代码更快地启动;在函数外部,kv 的每个存储和加载都涉及 dict 查找,而在函数内部,Python 可以(并且 CPython 参考解释器确实)使用简单的 C 数组查找和在编译时计算的索引。

第三种方法是作弊方法:确保kv预先存在,所以globals()dict不会添加或删除项目。您只需要添加:

k = v = None

就在 for 循环之前。