Iteratoren in Python
Ein Iterator ist ein Objekt, von dem wiederholt Objekte abgerufen werden können (eine Objektquelle, ein Spender für Objekte).
Englisch: “iterate ” + “-or ” = “iterator ”, „etwas, das wiederholt“ – hier: „etwas, das wiederholt gibt“.
Falls ein Iterator einmal kein Objekt mehr liefern kann, führt der Versuch des Abrufs eines Objekts zu einem Laufzeitfehler vom Typ »StopIteration«.
Englisch: “iterate ” + “-ion ” = “iteration ”, „Wiederholung“. Das Wort „Iteration“ steht sogar im Duden! (online-Duden, Stand: 2020)
Wir nehmen einmal an, ›t‹ sei ein Iterator.
Dann könnte ›t‹ beispielsweise
- beim ersten Abruf ein Objekt mit dem Wert «2» liefern,
- beim nächsten Abruf ein Objekt mit dem Wert «1»,
- beim nächsten Abruf ein Objekt mit dem Wert «0» und dann,
- könnte der nächste Abruf zu einem Laufzeitfehler vom Typ »StopIteration« führen.
Weitere Abrufe würden dann immer wieder zu einem Laufzeitfehler vom Typ »StopIteration« führen. Der Iterator wäre „verbraucht“.
Man kann sich einen Iterator wie einen Nummernspender vorstellen, wie er in manchen Warteräumen hängt. Bei jeder Betätigung eines Tasters erhält man ein Kärtchen (Objekt) mit einer neuen Wartenummer, solange bis der Vorrat an Kärtchen verbraucht (»StopIteration«) ist.
Wir uns daher oft vor, daß ein Iterator, die Objekte „enthält“, die man von ihm abrufen kann (obwohl diese nicht unbedingt immer alle direkt in dem Iterator gespeichert sein müssen, es kann auch sein, daß sie erst bei Bedarf erzeugt werden).
Die Funktion ›next‹
Um zu versuchen, ein Objekt von einem Iterator zu erhalten, ruft man in Python die Funktion ›next‹ mit dem Iterator als Argument auf.
- Konsolenprotokoll
from multiprocessing.pool import job_counter
next( job_counter )
0
next( job_counter )
1
next( job_counter )
2
Die Funktion ›next‹ ist eine Wertwirkfunktion : Ihr Wert ist das aktuelle Objekt des Iterators, und die von ihr bewirkte Änderung besteht darin, den Iterator auf das nächste Objekt weiterzustellen. (Sie ähnelt damit der Funktion ›random‹, die man als eine Quelle von Zufallszahlen ansehen kann.)
Ein weiteres Beispiel für einen Iterator (1)
Ebenfalls ein Iterator ist das Objekt ›stdin‹ aus dem Modul ›sys‹. Es liefert jeweils die nächste Eingabezeile (einschließlich das Zeilenendes) als Zeichenfolge. Daher ist es nötig, nach der Auswertung von »next( stdin )« eine Zeile einzugeben (mit der Eingabetaste) bevor das Programm weiterläuft.
- Konsole
from sys import stdin
"Eingabe = " + next( stdin )
abc
Eingabe = abc\n
Durch »\n« wird symbolisch das Zeilenendzeichen (entsprechend der Eingabetaste) angezeigt, daß bei Verwendung von »next( stdin )« ebenfalls noch zu der Eingabe gehört.
Durch Eingabe von Strg-Z („^Z“, Strg-D unter Linux ) kann ein Benutzer mitteilen, daß er keine weiteren Eingaben mehr hat. Dies führt dann zu einem Laufzeitfehler vom Typ »StopIteration«. Dadurch wird ausgedrückt, daß derzeit keine weiteren Werte mehr verfügbar sind.
- Konsole
"Eingabe = " + next( stdin )
^Z
StopIteration
Erkennen von Iteratoren
In der Regel erfährt man aus der Dokumentation einer Bibliothek, welche Objekte Iteratoren sind, falls man diese Information zur Benutzung der Bibliothek benötigt. Daher ist es nur selten nötig, durch ein Programm zu ermitteln, ob ein Objekt ein Iterator ist.
Wir können ›next‹ versuchsweise mit einem Objekt aufrufen, um zu erfahren, ob das Objekt ein Iterator ist.
- Konsole
next( True )
TypeError: 'bool' object is not an iterator
Die Meldung »TypeError: 'bool' object is not an iterator« sagt deutlich, daß das Objekt ›2‹ kein Iterator ist.
Solche Tests des „Erkennens durch Ausprobieren“ finden ihre Grenzen in einem Programm aber darin, daß ein Aufruf wie »next( stdin )« ein Programm dauerhaft blockieren könnte (wenn niemand etwa eingibt) oder daß durch Aufruf von »next« sogar schädliche Folgen ausgelöst werden könnten.
Rückblende Wir hatten schon die Begriffe „System“ und „Zustand“ kennengelernt. Ein Iterator ist solch ein System, mit einem veränderlichen Zustand.
Anwendungsgebiete von Iteratoren
- Iteratoren werden verwendet, um eine Quelle darzustellen, von der nacheinander einzelne Werte abgerufen werden können.
- Es muß nicht von vorneherein klar sein, wie viele Werte es ingesamt gibt (Daher kann man auch ›len‹ nicht verwenden). Es ist möglich, daß eine Iterator den nächsten Wert erst ermittelt, wenn dieser abgerufen wird und dieser Wert zuvor noch gar nicht festgelegt war.
- Es kann sein, daß der Iterator erschöpft ist, dann kommt der Laufzeitfehler ›StopIteration‹.
- Es kann sein, daß ein Iterator unerschöpflich ist und immer neue Werte liefert. Im Gegensatz zu Tupel oder Listen, bei denen alle enthaltenen Werte gleichzeitig gespeichert werden, können Iteratoren auch unendlich viele Werte darstellen, zum Beispiel alle natürlichen Zahlen ab 0.