字典无限循环意外退出
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
将是更新字典的值为 None
的新键。
- 我输出当前计数器。
在我看来,这应该以一种无限循环的方式输出自然数:
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()
应该足以获取该对并将其从字典中删除。由于在字典清空后立即添加下一个整数,因此在评估时循环条件永远不会为假,从而导致无限循环。
我正在尝试在 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
将是更新字典的值为None
的新键。 - 我输出当前计数器。
在我看来,这应该以一种无限循环的方式输出自然数:
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()
应该足以获取该对并将其从字典中删除。由于在字典清空后立即添加下一个整数,因此在评估时循环条件永远不会为假,从而导致无限循环。