Словарь бесконечного цикла неожиданно завершает работу - PullRequest
0 голосов
/ 23 января 2019

Я экспериментировал с различными способами создания бесконечного цикла в 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. Я вывожу текущий счетчик.

Это, в моей голове, должно выводить натуральные числа в некотором родес бесконечным циклом,Ясно, что в блоке кода цикла нет базового условия или значения часового значения, так почему же Python выполняет этот код только 5 раз?

Ответы [ 4 ]

0 голосов
/ 23 января 2019

Как отмечали многие, изменение структуры данных во время итерации с помощью цикла 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 словарь ложный, когда он пустой (см. документы ), поэтому цикл остановится, только если в начале итерации x пуст. Поскольку в словаре имеется только одна пара ключ-значение, popitem() должно быть достаточно, чтобы получить эту пару и удалить ее из словаря. Поскольку следующее целое число добавляется сразу после опустошения словаря, условие цикла никогда не будет ложным при оценке, что приведет к бесконечному циклу.

0 голосов
/ 23 января 2019

Я только что проверил ваш код на 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 создает достаточно памяти для этого ключевого значения и еще несколько, и после того, как он израсходован, больше нет памяти, выделенной для вашего словаря.

0 голосов
/ 23 января 2019

В этом случае, как писал @benvc, это не гарантируется. Но если вам интересно, почему это работает в C-Python:

Реализация C-Python уничтожает объект dict после некоторых вставок и копирует его в новое пространство в памяти. Это не заботится об удалениях. Поэтому, когда это происходит, цикл замечает это и разрывается с исключением.

Перейдите по этой ссылке, если вы хотите узнать больше об этом, а также о многих других интересных внутренностях Python здесь.

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

0 голосов
/ 23 января 2019

Нет гарантии, что вы будете перебирать все свои записи в dict, если будете изменять его в цикле.Из документов :

Итерация представлений при добавлении или удалении записей в словаре может вызвать ошибку RuntimeError или не удастся перебрать все записи.

Вы можете создать «перечислимый» бесконечный цикл, аналогичный вашей первоначальной попытке, используя 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
...