字典无限循环意外退出

时间:2019-01-22 21:16:07

标签: python dictionary infinite-loop

我正在尝试用各种方法在Python中创建无限循环(除了通常的2019-01-22 13:29:32.897893-0800 test[86762:4812871] Starting <MyOperation: 0x10058d2d0> 2019-01-22 13:29:32.897893-0800 test[86762:4812872] Starting <MyOperation: 0x10058d710> 2019-01-22 13:29:32.897903-0800 test[86762:4812873] Starting <MyOperation: 0x100589930> 2019-01-22 13:29:32.898161-0800 test[86762:4812871] Starting <MyOperation: 0x10058edc0> 2019-01-22 13:29:32.898166-0800 test[86762:4812873] Starting <MyOperation: 0x10058ed50> 2019-01-22 13:29:37.898487-0800 test[86762:4812872] Finished <MyOperation: 0x100589930> 2019-01-22 13:29:37.898489-0800 test[86762:4812870] Finished <MyOperation: 0x10058ed50> 2019-01-22 13:29:37.898548-0800 test[86762:4812874] Finished <MyOperation: 0x10058edc0> 2019-01-22 13:29:37.898797-0800 test[86762:4812870] Starting <MyOperation: 0x100590000> 2019-01-22 13:29:37.899160-0800 test[86762:4812870] Finished <MyOperation: 0x10058d710> 2019-01-22 13:29:37.899651-0800 test[86762:4812870] Starting <MyOperation: 0x1005901a0> 2019-01-22 13:29:37.899933-0800 test[86762:4812874] Starting <MyOperation: 0x100590340> 2019-01-22 13:29:37.900133-0800 test[86762:4812871] Finished <MyOperation: 0x10058d2d0> 2019-01-22 13:29:37.900504-0800 test[86762:4812871] Starting <MyOperation: 0x100590680> 2019-01-22 13:29:37.900583-0800 test[86762:4812874] Starting <MyOperation: 0x1005904e0> 2019-01-22 13:29:42.899325-0800 test[86762:4812871] Finished <MyOperation: 0x100590000> 2019-01-22 13:29:42.899541-0800 test[86762:4812874] Starting <MyOperation: 0x100590820> 2019-01-22 13:29:43.393291-0800 test[86762:4812871] Finished <MyOperation: 0x1005901a0> 2019-01-22 13:29:43.393298-0800 test[86762:4812874] Finished <MyOperation: 0x100590340> 2019-01-22 13:29:43.394531-0800 test[86762:4812874] Finished <MyOperation: 0x1005904e0> 2019-01-22 13:29:43.395380-0800 test[86762:4812874] Finished <MyOperation: 0x100590680> 2019-01-22 13:29:43.396359-0800 test[86762:4812874] Starting <MyOperation: 0x1005909c0> 2019-01-22 13:29:43.397440-0800 test[86762:4812872] Starting <MyOperation: 0x100590b60> 2019-01-22 13:29:43.397891-0800 test[86762:4812874] Starting <MyOperation: 0x100590d00> 2019-01-22 13:29:43.399711-0800 test[86762:4812872] Starting <MyOperation: 0x100590ea0> 2019-01-22 13:29:47.900058-0800 test[86762:4812984] Finished <MyOperation: 0x100590820> 2019-01-22 13:29:48.892953-0800 test[86762:4812872] Finished <MyOperation: 0x100590d00> 2019-01-22 13:29:48.892970-0800 test[86762:4812871] Finished <MyOperation: 0x100590b60> 2019-01-22 13:29:48.893019-0800 test[86762:4813163] Finished <MyOperation: 0x100590ea0> 2019-01-22 13:29:48.893562-0800 test[86762:4812984] Finished <MyOperation: 0x1005909c0> Program ended with exit code: 0 以外),并提出了这个想法:

while True

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

  1. 我在字典中遍历键的值
  2. 我删除了该条目。
  3. 循环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的新关键字,它将更新字典。
  4. 我输出当前计数器。

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

None

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

0
1
2
3
4
5
.
.
.

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

4 个答案:

答案 0 :(得分:43)

如果您在循环中对其进行了突变,则不能保证将迭代所有dict条目。来自docs

  

在添加或删除字典中的条目时迭代视图可能   引发RuntimeError或无法遍历所有条目。

您可以使用Flowable.create<Int>({ emmit -> loadNewListener = object :Listener { override fun onEmit(id: Int) { emmit.onNext(id) } } }, BackpressureStrategy.LATEST) .debounce(500, TimeUnit.MILLISECONDS) .subscribe({ loadNew(id = it.id) }, { Timber.e("") }) 创建一个类似于您初次尝试的“枚举”无限循环。例如:

itertools.count()

答案 1 :(得分:8)

在这种情况下,就像@benvc所写的那样,这不能保证能正常工作。但是,如果您想知道为什么它不能在C-Python中工作:

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

如果您想了解更多有关此内容以及此处其他许多有趣的python内部信息的信息,请查看此链接。

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

答案 2 :(得分:5)

我刚刚在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为该键值创建了足够的内存,并为它创造了更多的内存,一旦用完,就不会再为字典分配更多的内存了。

答案 3 :(得分:3)

许多人指出,在迭代过程中使用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()应该足以获取该对并将其从字典中删除。由于在清空字典后立即添加下一个整数,因此求值时循环条件永远不会为假,从而导致无限循环。