【问题标题】:Python: globals().items() iterations try to change a dictPython:globals().items() 迭代尝试更改字典
【发布时间】:2019-08-17 12:20:23
【问题描述】:

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 k, v in list(globals().items()):
  • 我不想创建新列表,因为我预计会有很多项目
  • “手数”是多少?无论如何,请参阅我的编辑以了解两种选择

标签: python python-import


【解决方案1】:

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

您可以通过在迭代之前显式复制 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 和从v 加载都涉及dict 查找,而在函数内部,Python 可以(以及 CPython 参考解释器)使用简单的 C 数组查找和计算的索引在编译时。

第三种方法是欺骗方法:确保kv事先存在,这样globals()dict就不会添加或删除项目。您只需要添加:

k = v = None

就在for 循环之前。

【讨论】:

  • 是的,这就是答案。k = v = None 很酷:D 还有任何想法为什么 print([ (k, v) for k, v in globals().items() if isinstance(v , 实体)]) 叫 TWICE?
  • @snamef:不应该,除非你真的运行了两次。但是您只显示了一行代码,我不知道您可能还做了什么来导致它(或导致类似的事情,例如不小心将globals() 的副本嵌套到看起来几乎像没有换行符的输出加倍以单独分解)。
【解决方案2】:

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

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

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

另一方面,列表推导式创建自己的本地函数范围。做吧:

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

如果你不想复制,那么做:

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

或者更好的是,将这一切包装在一个函数中,这样您就不会意外触及全局命名空间

【讨论】:

    猜你喜欢
    • 2018-02-10
    • 2011-04-06
    • 1970-01-01
    • 2021-11-21
    • 2013-02-22
    • 1970-01-01
    • 1970-01-01
    • 2019-06-07
    • 2019-12-02
    相关资源
    最近更新 更多