字典无限循环意外退出

Dictionary infinite loop is exiting unexpectedly

我正在尝试在 Python 中创建无限循环的各种方法(不同于通常的 while True),并提出了这个想法:

x = {0: None}

for i in x:
    del x[i]
    x[i+1] = None  # Value doesn't matter, so I set it to None
    print(i)

在纸上,我找出了无限循环的方式:

  1. 我循环遍历字典中的键值
  2. 我删除那个条目。
  3. 循环中的当前计数器位置 + 1 将是更新字典的值为 None 的新键。
  4. 我输出当前计数器。

在我看来,这应该以一种无限循环的方式输出自然数:

0
1
2
3
4
5
.
.
.

我认为这个想法很聪明,但是当我在 Python 3.6 上 运行 它时,它输出:

0
1
2
3
4

是的,它在 5 次迭代后以某种方式停止了。显然,循环的代码块中没有基本条件或标记值,那么为什么 Python 只 运行ning 这段代码 5 次?

如果你在循环中改变它,不能保证你会迭代所有的字典条目。来自 docs:

Iterating views while adding or deleting entries in the dictionary may raise a RuntimeError or fail to iterate over all entries.

您可以创建一个 "enumerated" 无限循环,类似于您使用 itertools.count() 的初始尝试。例如:

from itertools import count

for i in count():
    print(i)
    # don't run this without some mechanism to break the loop, i.e.
    # if i == 10:
    #     break

# OUTPUT
# 0
# 1
# 2
# ...and so on

我刚刚在 python2 和 python3

中测试了您的代码
python3 output
0,1,2,3,4
python2
0,1,2,3,4,5,6,7

想到一件事可能会发生。当您创建第一个键值时,您的字典中只会分配一定数量的内存,而当您删除键值时,我们不会分配任何内存或取消分配您只是删除该值的内存。一旦使用了所有分配的内存,它就会退出。因为如果你 运行 没有那个 del 你会得到这个错误

RuntimeError: dictionary changed size during iteration

因此 python 为该键值创建了足够的内存,并创建了更多的内存,一旦用完,就不会为您的字典分配更多的内存。

在这种情况下,正如@benvc 所写,这不能保证有效。但是如果您想知道为什么它在 C-Python:

中有效

C-Python 实现在一些插入后销毁 dict 对象并将其复制到内存中的新 space。它不关心删除。所以当这种情况发生时,循环会注意到它并异常中断。

如果您想阅读更多相关信息,请查看此 link 以及此处许多其他有趣的 python 内部信息。

https://github.com/satwikkansal/wtfpython#-modifying-a-dictionary-while-iterating-over-it

正如许多人指出的那样,在使用 for 循环迭代期间修改数据结构不是一个好主意。 while 循环确实允许这样做,因为它会在每次迭代时重新评估其循环条件(令我印象深刻的是,还没有人建议将其作为替代方案)。只需找到正确的循环条件即可。您的脚本必须变为:

x = {0: None}
while x:
    i, _ = x.popitem()
    print(i)
    # to avoid infinite loop while testing
    # if i == 10:
    #     break
    x[i+1] = None

在Python中,字典为空时为假(参见docs),因此只有在迭代开始时x为空时循环才会停止。 由于字典只有一个键值对,popitem() 应该足以获取该对并将其从字典中删除。由于在字典清空后立即添加下一个整数,因此在评估时循环条件永远不会为假,从而导致无限循环。