Бесконечный цикл в Python - обновляется список или нет? - PullRequest
0 голосов
/ 11 апреля 2019

Прежде всего, я знаю, что я должен использовать цикл while, чтобы создать бесконечный цикл.Тем не менее, играя с созданием бесконечного цикла for в Python, я сталкиваюсь с чем-то, что не до конца понимаю.

Моя идея "бесконечного" цикла for заключалась в следующем:

l = [1]
for el in l:
  print(len(l))
  l.append(1)

и это фактически создавало бесконечный цикл, так как 1 постоянно добавлялся в список l на каждой итерации цикла.Поэтому я подумал, что не хочу, чтобы мой список становился все длиннее и длиннее, чтобы в какой-то момент мне не хватило памяти.Итак, я сделал это:

l = [1]
for el in l:
  print(len(l))
  l.pop(0)
  l.append(1)

и получил всего одну итерацию.Может кто-нибудь объяснить, почему?Это потому, что l все еще ссылается на тот же объект, а его длина всегда равна 1?

Ответы [ 2 ]

1 голос
/ 11 апреля 2019

Оператор for возвращает «итератор», который является контейнером для потока данных, который возвращает по одному элементу за раз, поэтому вы не выполняете итерации по итерируемой (ваш список в данном случае), новместо этого над этим контейнером.

Вы можете вернуть этот итератор напрямую, используя iter(), чтобы лучше понять, что происходит с вашим циклом for.

items = ['a']

it = iter(items)
print(it.__reduce__())
# (<built-in function iter>, (['a'],), 0)

Встроенная функция __reduce__()возвращает информацию о состоянии итератора.Обратите внимание, что информация о состоянии, возвращаемая сразу после создания итератора, включает в себя итерируемое значение ['a'] и значение индекса 0.Значение индекса указывает позицию следующего элемента, который будет возвращен итератором.

Для имитации первой итерации цикла мы можем использовать next(), который возвращает следующий элемент из итератора.

next(it)
print(it.__reduce__())
# (<built-in function iter>, (['a'],), 1)

Теперь мы видим, что значение индекса изменилось на 1, поскольку итератор уже возвратил первый элемент в списке и на следующей итерации попытается вернуть второй элемент в списке.Если вы попытаетесь удалить первый элемент в списке, а затем добавите другой элемент в список, следующим будет итоговое состояние итератора.

items.pop(0)
items.append('b')
print(it.__reduce__())
# (<built-in function iter>, (['b'],), 1)

Вы видите, что первый элемент был удален, а новыйэлемент был добавлен (как и ожидалось).Однако итератор все еще сохранил значение индекса 1 в качестве позиции следующего элемента, который будет возвращен из итерации.Если мы попытаемся сделать еще одну итерацию, то будет сгенерировано исключение StopIteration, поскольку в итерируемом элементе с индексом 1 нет элемента, используемого нашим контейнером итератора.

next(it)
# Traceback (most recent call last):
#   File "main.py", line 16, in <module>
#     next(it)
# StopIteration

Если вы действительно заинтересованы в созданиибесконечный цикл for, использование генератора было бы лучшим способом решения ваших проблем с памятью (хотя, как вы отметили в своем вопросе, не слишком много веских причин не использовать while для такого рода вещей).См. мой ответ на связанный вопрос для примера бесконечного цикла for .

1 голос
/ 11 апреля 2019

Если вы используете итератор , как предложено выше, это упрощает реализацию.
В следующем классе реализован метод ___iter___ и ___next___, необходимый для классов итераторов

class WhileLoop:

    def __init__(self, value):
        #Assign the value
        self.value = value

    def __iter__(self):
        #The iterator will be the class itself
        return self

    def __next__(self):
        #The next element in the loop is the value itself
        return self.value

Затем вы можете вызвать и выполнить цикл для этого итератора следующим образом.

loop = WhileLoop(1)
for item in loop:
    print(item)
...