Python list zachowanie iteratora i następne (iterator)

147

Rozważać:

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

Tak więc, postęp iteratora jest, zgodnie z oczekiwaniami, obsługiwany przez mutację tego samego obiektu.

W takim przypadku spodziewałbym się:

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

pomijać co drugi element: wywołanie to nextpowinno przesuwać iterator jeden raz, a następnie niejawne wywołanie wykonane przez pętlę powinno przesuwać go po raz drugi - a wynik tego drugiego wywołania zostałby przypisany i.

Tak nie jest. Pętla wypisuje wszystkie pozycje z listy, bez pomijania żadnych.

Moją pierwszą myślą było to, że może się to zdarzyć, ponieważ pętla wywołuje iterto, co zostało przekazane, a to może dać niezależny iterator - tak nie jest, jak mamy iter(a) is a.

Dlaczego więc nextw tym przypadku nie wydaje się, aby przyspieszyć iterator?

lvc
źródło

Odpowiedzi:

197

To, co widzisz, to interpreter odbijający echo zwracanej wartości next()oprócz iwypisywania każdej iteracji:

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

Tak samo 0jest z wyjściem print(i), 1wartością zwracaną z next(), powtórzoną przez interaktywny interpreter itp. Jest tylko 5 iteracji, każda iteracja skutkuje zapisaniem 2 wierszy na terminalu.

Jeśli przypiszesz wynik działania next()rzeczy zgodnie z oczekiwaniami:

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

lub wydrukuj dodatkowe informacje, aby odróżnić print()wyjście od echa interpretera interaktywnego:

>>> 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

Innymi słowy, next()działa zgodnie z oczekiwaniami, ale ponieważ zwraca następną wartość z iteratora, powtórzoną przez interaktywny interpreter, można sądzić, że pętla ma w jakiś sposób własną kopię iteratora.

Martijn Pieters
źródło
13
Nie wiedziałem o tym zachowaniu tłumacza. Cieszę się, że odkryłem to, zanim straciłem dużo czasu, zastanawiając się nad tym podczas rozwiązywania jakiegoś prawdziwego problemu.
brandizzi
5
... * umiera *. Najgorsze jest to, że pamiętam, jak wspominałem komuś dokładnie o tym zachowaniu tłumacza, być może tydzień temu.
lvc
ciekawy. Próbowałem i w a: next (a); print i i pomyślałem, że przeskoczę do 1 i wydrukuję 1,3,5,7,9. Ale nadal jest to 0,2,4,6,8. Czemu?
user2290820
3
izostał już przydzielony. next(a)oznacza, że ​​następna iteracja 2jest przypisana do i, następnie idziesz adalej, drukujesz iitd.
Martijn Pieters
1
To nie działa, jeśli n jest dziwne - StopIterationexcepetio nis podniesione, gdy next(a)zostanie wywołane po usunięciu listy.
Raf
13

Dzieje się tak, że next(a)zwraca następną wartość a, która jest drukowana do konsoli, ponieważ nie ma na nią wpływu.

To, co możesz zrobić, to wpłynąć na zmienną o tej wartości:

>>> a = iter(list(range(10)))
>>> for i in a:
...    print(i)
...    b=next(a)
...
0
2
4
6
8
njzk2
źródło
8

Istniejące odpowiedzi są dla mnie trochę zagmatwane, ponieważ tylko pośrednio wskazują na zasadniczą tajemniczą rzecz w przykładzie kodu: zarówno * "print i", jak i "next (a)" powodują, że ich wyniki są drukowane.

Ponieważ drukują naprzemiennie elementy oryginalnej sekwencji i jest nieoczekiwane, że wypisuje się instrukcja „next (a)”, wygląda na to, że instrukcja „print i” wypisuje wszystkie wartości.

W tym świetle staje się bardziej jasne, że przypisanie wyniku „next (a)” do zmiennej blokuje drukowanie jej wyniku, tak że drukowane są tylko wartości alternatywne, które są wypisywane ze zmiennej pętli „i”. Podobnie, sprawienie, że instrukcja „print” emituje coś bardziej charakterystycznego, również ją ujednoznacznia.

(Jedna z istniejących odpowiedzi odrzuca inne, ponieważ ta odpowiedź ma przykładowy kod oceniany jako blok, więc interpreter nie zgłasza wartości pośrednich dla „next (a)”).

Ogólnie rzecz biorąc, czarujące w odpowiadaniu na pytania jest wyraźne mówienie o tym, co jest oczywiste, gdy już znasz odpowiedź. To może być nieuchwytne. Podobnie krytykujesz odpowiedzi, kiedy je zrozumiesz. To interesujące...

klm
źródło
2

Coś jest nie tak z Twoim Pythonem / komputerem.

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

>>> 
0
2
4
6
8

Działa zgodnie z oczekiwaniami.

Przetestowano w Pythonie 2.7 i Pythonie 3+. Działa poprawnie w obu

Inbar Rose
źródło
5
Otrzymuję ten sam wynik, co @lvc (tylko na IDLE, ale po uruchomieniu jako skrypt dostaję to))
jamylak
3
@Inbar Rose Tylko jeśli uruchamiasz jako skrypt.
Quintec
1
jest to zachowanie polegające na umieszczaniu kodu za pomocą powłoki interaktywnej. Jeśli funkcja zwraca wartość bez używane, interpreter będzie drukować to zapłacić jako wyjście debugowania
Reishin
2

Dla tych, którzy nadal nie rozumieją.

>>> 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

Jak już powiedzieli inni, nextzgodnie z oczekiwaniami zwiększa iterator o 1. Przypisanie zwracanej wartości do zmiennej nie zmienia magicznie jej zachowania.

Wesley
źródło
1

Zachowuje się tak, jak chcesz, jeśli zostanie wywołany jako funkcja:

>>> def test():
...     a = iter(list(range(10)))
...     for i in a:
...         print(i)
...         next(a)
... 
>>> test()
0
2
4
6
8
burmer
źródło