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()вихід від інтерактивного перекладача:

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

5
... * вмирає *. Найгірше це, я можу згадати, що саме таку поведінку перекладача згадував хтось, можливо, тиждень тому.
lvc

цікаво. Я спробував для i в a: next (a); надрукувати i і подумав, що я перескочу до 1 і надрукую 1,3,5,7,9. Але все одно це 0,2,4,6,8. Чому?
користувач2290820

3
iбуло вже призначено. next(a)означає, що наступна ітерація 2призначена i, потім ви aзнову рухаєтесь , друкуєте iтощо
Martijn Pieters

1
Це не працює, якщо n є непарним - StopIterationexcepetio nis підвищується, коли 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

Я вважаю наявні відповіді трохи заплутаними, оскільки вони лише побічно вказують на істотну містифікуючу річ у прикладі коду: і «друк i», і «наступний (а)» викликають друк їх результатів.

Оскільки вони друкують чергуючі елементи вихідної послідовності, і несподівано, що друкується оператор "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 Тільки якщо ви працюєте як сценарій.
Квінтек

1
це поведінка введення коду через інтерактивну оболонку. Якщо функція повертає значення, не використовуючи її, інтерпретатор виводить її в оболонку як вихід налагодження
Reishin

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.