Прежде всего, я знаю, что я должен использовать цикл 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?

1
user2300369 11 Апр 2019 в 18:09

2 ответа

Лучший ответ

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

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

1
benvc 16 Апр 2019 в 20:00

Если вы используете итератор, как это было предложено выше, это облегчит его работу к выполнению.
Класс ниже реализует метод ___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)
1
Devesh Kumar Singh 11 Апр 2019 в 16:10