Поведение итератора списка Python и следующий (итератор)


147

Рассматривать:

>>> lst = iter([1,2,3])
>>> next(lst)
1
>>> next(lst)
2

Таким образом, продвижение итератора, как и ожидалось, обрабатывается путем изменения этого же объекта.

В таком случае я бы ожидал:

a = iter(list(range(10)))
for i in a:
   print(i)
   next(a)

пропустить каждый второй элемент: вызов nextдолжен продвинуть итератор один раз, затем неявный вызов, сделанный циклом, должен продвинуть его во второй раз - и результат этого второго вызова будет присвоен i.

Это не так. Цикл печатает все элементы в списке, не пропуская ни одного.

Моей первой мыслью было, что это может произойти, потому что цикл вызывает iterто, что он передается, и это может дать независимый итератор - это не тот случай, как у нас iter(a) is a.

Итак, почему nextне появляется, чтобы продвинуть итератор в этом случае?

Ответы:


197

То, что вы видите, это интерпретатор, возвращающий возвращаемое значение next()в дополнение к iвыводу на печать каждой итерации:

>>> a = iter(list(range(10)))
>>> for i in a:
...    print(i)
...    next(a)
... 
0
1
2
3
4
5
6
7
8
9

Как 0и результат print(i), 1возвращаемое значение next(), отраженное интерактивным интерпретатором и т. Д. Существует всего 5 итераций, каждая из которых приводит к записи 2 строк в терминал.

Если вы назначите вывод next()вещей работать как ожидалось:

>>> a = iter(list(range(10)))
>>> for i in a:
...    print(i)
...    _ = next(a)
... 
0
2
4
6
8

или напечатайте дополнительную информацию, чтобы отличить print()вывод от интерактивного интерпретатора echo:

>>> a = iter(list(range(10)))
>>> for i in a:
...    print('Printing: {}'.format(i))
...    next(a)
... 
Printing: 0
1
Printing: 2
3
Printing: 4
5
Printing: 6
7
Printing: 8
9

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


13
Я не знал об этом поведении от переводчика. Я рад, что обнаружил это, прежде чем терять много времени, размышляя об этом, решая какую-то реальную проблему.
brandizzi

5
... * умирает *. Хуже всего то, что я помню, что упомянул именно это поведение переводчика кому-то, возможно, неделю назад.
LVC

интересный. Я попытался для i в: next (a), напечатал i и подумал, что прыгну в 1 и напечатаю 1,3,5,7,9. Но все равно это 0,2,4,6,8. Зачем?
user2290820

3
iбыл уже назначен. next(a)означает, что следующей итерации 2назначена i, затем вы aснова двигаетесь вперед, печатаете iи т. д.
Мартин Питерс

1
Это не работает, если n нечетно - StopIterationисключение возникает при next(a)вызове после исчерпания списка.
Раф

13

Происходит следующее: next(a)возвращается следующее значение a, которое выводится на консоль, поскольку на него это не влияет.

Что вы можете сделать, это повлиять на переменную с этим значением:

>>> a = iter(list(range(10)))
>>> for i in a:
...    print(i)
...    b=next(a)
...
0
2
4
6
8

8

Я нахожу существующие ответы немного запутанными, потому что они только косвенно указывают на существенную загадочную вещь в примере кода: и «print i», и «next (a)» вызывают печать их результатов.

Поскольку они печатают чередующиеся элементы исходной последовательности, и неожиданно, что оператор «next (a)» печатает, создается впечатление, что оператор «print i» печатает все значения.

В этом свете становится более ясным, что присвоение результата «next (a)» переменной запрещает печать ее «результата», так что печатаются только альтернативные значения, которые переменная цикла «i». Точно так же, заставляя выражение «print» выдавать что-то более характерное, оно также устраняет неоднозначность.

(Один из существующих ответов опровергает другие, поскольку в этом ответе пример кода оценивается как блок, так что интерпретатор не сообщает промежуточные значения для «next (a)».)

В общем, соблазнительная вещь при ответе на вопросы - это явное понимание того, что очевидно, когда вы знаете ответ. Это может быть неуловимым. Точно так же критикуйте ответы, как только вы их понимаете. Это интересно...


2

Что-то не так с вашим Python / Computer.

a = iter(list(range(10)))
for i in a:
   print(i)
   next(a)

>>> 
0
2
4
6
8

Работает как ожидалось.

Протестировано в Python 2.7 и в Python 3+. Работает нормально в обоих


5
Я получаю тот же результат, что и @lvc (но только на IDLE, когда выполняется как скрипт, я получаю это))
jamylak

3
@Inbar Rose Только если вы запускаете как скрипт.
Quintec

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

2

Для тех, кто до сих пор не понял.

>>> a = iter(list(range(10)))
>>> for i in a:
...    print(i)
...    next(a)
... 
0 # print(i) printed this
1 # next(a) printed this
2 # print(i) printed this
3 # next(a) printed this
4 # print(i) printed this
5 # next(a) printed this
6 # print(i) printed this
7 # next(a) printed this
8 # print(i) printed this
9 # next(a) printed this

Как уже говорили другие, nextувеличивает итератор на 1, как и ожидалось. Присвоение возвращенного значения переменной не изменяет его поведение.


1

Он ведет себя так, как вы хотите, если вызывается как функция:

>>> def test():
...     a = iter(list(range(10)))
...     for i in a:
...         print(i)
...         next(a)
... 
>>> test()
0
2
4
6
8
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.