цикл for с использованием enumerate неожиданно завершается

Вот простой цикл for по перечисляемому объекту. Это заканчивается из-за (эта строка, которую я упомянул в качестве комментария). Почему это?

enum_arr = enumerate(arr)
for ele in enum_arr:
    print(ele)
    print(list(enum_arr)[ele[0]:]) # terminates due to this line

Выход:

(0, 0)
[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]

Если я закомментирую второй оператор печати, то:

Выход:

(0, 0)
(1, 1)
(2, 2)
(3, 3)
(4, 4)
(5, 5) 

Как и ожидалось. Почему это происходит?


person Sharvari Gc    schedule 09.10.2019    source источник
comment
enumerate() — это итератор. Его можно зациклить только один раз. list() перебирает итератор.   -  person Martijn Pieters    schedule 09.10.2019


Ответы (1)


enumerate() дает вам объект iterator. Итераторы подобны закладке в книге, которую можно сдвинуть только вперед; как только вы дойдете до конца книги, вы больше не сможете вернуться назад и должны будете сделать новую закладку.

Затем вы используете этот итератор в двух местах; цикл for и list(). Функция list() переместила закладку полностью в конец, поэтому цикл for не может переместить ее дальше.

Вам придется создать новый объект enumerate() в цикле, если вы хотите использовать отдельный независимый итератор:

enum_arr = enumerate(arr)
for ele in enum_arr:
    print(ele)
    print(list(enumerate(arr[ele[0]:], ele[0])))

Это требует, чтобы arr сам был не итератором, это должна быть последовательность, чтобы вы могли индексировать ее. Я предполагаю, что у вас есть список, кортеж, диапазон или подобное значение.

Обратите внимание, что я передал ele[0] дважды, второй аргумент enumerate() позволяет установить начальное значение счетчика.

Здесь проще использовать присваивание кортежа, чтобы разделить количество и значение:

for count, value in enum_arr:
    print((count, value))
    print(list(enumerate(arr[count:], count)))

Демо:

>>> arr = range(6)
>>> enum_arr = enumerate(arr)
>>> for count, value in enum_arr:
...     print((count, value))
...     print(list(enumerate(arr[count:], count)))
...
(0, 0)
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]
(1, 1)
[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]
(2, 2)
[(2, 2), (3, 3), (4, 4), (5, 5)]
(3, 3)
[(3, 3), (4, 4), (5, 5)]
(4, 4)
[(4, 4), (5, 5)]
(5, 5)
[(5, 5)]

Возвращаясь к аналогии с книгой и требованию, чтобы arr была последовательностью: пока arr является книгой с номерами страниц, вы можете добавлять дополнительные закладки в любой момент. Если это какой-то другой итерируемый тип, вы не можете индексировать в него, и поэтому придется найти какие-то другие средства, чтобы «пропустить вперед» и обратно. Продолжая аналогию: скажем, книга передается вам в потоковом режиме, по одной странице за раз, и вы не можете вернуться назад после того, как получили все страницы. Решение может состоять в том, чтобы сначала создать локальный кеш страниц; если вы можете сэкономить память, которую можно было бы сделать с cached_copy = list(arr). Просто примите во внимание, что вы должны быть уверены, что книга, которую вы получаете, не настолько длинная, чтобы требовать больше места, чем у вас есть на самом деле. А некоторые итерации бесконечны, поэтому потребуется бесконечная память!

person Martijn Pieters    schedule 09.10.2019
comment
Потрясающий. Спасибо. Вы очень хорошо и быстро ответили. - person Sharvari Gc; 09.10.2019