Iteratie als interface · oefening
Gebruik iter() en next()
Een for-lus is een gemak. Daaronder zitten twee functieaanroepen die je zelf kunt doen. Door ze één keer zelf uit te voeren, verdwijnt het grootste deel van het mysterie.
Een lus met de hand aansturen
iter() vraagt om de iterator. next() vraagt die om één item en verplaatst die vooruit. Dat is het hele protocol aan de kant van de aanroeper.
Vraag nog een keer en de iterator moet iets zeggen:
next(walker)
StopIteration. Geen bug, geen fout in je code: zo meldt een iterator dat er niets meer is. De exceptie is het bericht.
Wat for doet
Als je die twee aanroepen kent, kun je de lus volledig uitschrijven:
Dezelfde uitvoer als for question in questions. Elke for-lus die je ooit schreef doet dit. Daarom gooit een for-lus nooit StopIteration naar je op: die opvangen en stoppen is de taak van de lus.
Wat doet een for-lus met StopIteration?
Een standaardwaarde in plaats van een exceptie
next() accepteert een tweede argument, dat wordt teruggegeven in plaats van een exceptie op te gooien wanneer de iterator klaar is:
Dit is nuttig om te weten, omdat het ‘neem het volgende ding, als er een is’ van vier regels terugbrengt tot één. Het is het juiste hulpmiddel wanneer opraken een gewone uitkomst is, en het verkeerde wanneer opraken betekent dat er iets misging en je dat wilt horen.
Waar dit echt voor dient
Meestal moet je for schrijven en deze les beweert niet anders. Rechtstreeks iter() en next() gebruiken is nuttig in een paar concrete situaties:
Eén item nemen en daarna over de rest itereren. De eerste regel van een bestand is een kop en de rest zijn gegevens:
lines = iter(all_lines)
header = next(lines)
for line in lines:
...
De for-lus gaat verder waar next() de positie achterliet, omdat er één iterator is die beide delen. Dat werkt alleen dankzij de scheiding uit les 1.
Veilig één item nemen. next(walker, None) beantwoordt ‘is er iets?’ en geeft je tegelijk dat item. De iterator gaat vooruit; dit is lezen, niet even kijken.
Twee reeksen met verschillende snelheden doorlopen. Wanneer zip() niet past omdat één kant soms twee keer vooruitgaat.
Als je merkt dat je while True schrijft met een try rond next(), heb je for herschreven. Schrijf for.
Opdracht
Gebruik iter() en next() rechtstreeks voor de drie taken waarbij ze hun plek verdienen.
split_header(lines) geeft de eerste regel en een lijst met de rest terug. Neem één item met next() en loop daarna over dezelfde iterator voor de rest. Lege invoer geeft (None, []) terug.
first_or(items, fallback) geeft het eerste item terug of fallback als er geen item is. Gebruik de vorm van next() met twee argumenten in plaats van een try.
count_by_hand(items) telt de items met while True, next() en except StopIteration in plaats van een for-lus. Je zou dit niet in echte code schrijven. Het één keer schrijven zorgt ervoor dat de lus geen magie meer is.
Voer het programma uit en controleer of split_header de twee resterende regels teruggeeft, niet alle drie.