Python Iterators – Funktionsweise und Nutzung im Überblick
Python Iterators sind Python Objekte mit einer zählbaren Menge an Elementen, die das Iterator-Protokoll bestehend aus den beiden Funktionen __iter__() und __next__() implementieren.
Was genau sind Python Iterators?
Bei Python Iterators handelt es sich in erster Linie um eine spezielle Form von Python-Objekten. Sie zeichnen sich dadurch aus, dass sie aus einer Menge von zählbaren Elementen bestehen. Somit können Sie die Elemente aus einem Iterator zählen und über alle Elemente eines Python Iterators iterieren.
Python ist eine hervorragende Programmiersprache für Webprojekte. Bei Ihrem Webprojekt unterstützt Deploy Now Sie durch automatisches Deployment und Building via GitHub, sodass Sie jederzeit den vollen Überblick behalten.
Iterators vs. Iterables
Python Iterators sind nicht zu verwechseln mit Python Iterables. Allerdings stehen der Iterator und das Iterable im engen Bezug zueinander. Die verschiedenen Iterables wie die Python List zeichnen sich dadurch aus, dass sie eine __iter__()-Methode besitzen und somit das Iterieren durch sie ermöglichen. Als Faustregel können Sie sich merken: Alles, was beim Aufruf einer for-Loop auf der rechten Seite des Schleifenkopfes steht, ist ein Iterable.
l = [1, 2, 3]
for x in l:
print(x)
Wie Sie sehen, taucht die Liste, die in der Variable „l“ hinterlegt ist, im Aufruf der Python-for-Schleife auf der rechten Seite des Schleifenkopfes hinter dem Schlüsselwort „in“ auf und ist somit ein Iterable.
Aus einem Iterable können Sie einen Python Iterator ableiten. Um sich das zu verdeutlichen und die Unterschiede zu verstehen, hilft ein Codebeispiel:
string = "test"
iteratorobjekt = iter(string)
next(iteratorobjekt)
iter(iteratorobjekt)
Zunächst wird im Code ein Python String mit dem Wert „test“ in der Variable namens „string“ hinterlegt. Strings fallen ebenfalls in die Kategorie der Iterables, denn Sie können durch jeden Buchstaben der Zeichenkette iterieren.
Dass Sie durch Strings iterieren können, heißt im Grunde nichts anderes, als dass ein String ebenfalls die __iter__()-Funktion unterstützt. Das sehen Sie auch in der nächsten Codezeile, in der wir einen Python Iterator dadurch erstellen, dass wir die iter-Funktion zusammen mit dem zuvor erstellten String aufrufen. Diese gibt einen Pointer auf den Strings zurück und speichert ihn in der Variable namens „iteratorobjekt“. Der Python Iterator befindet sich also in einem Zustand. Diesen Zustand können wir verändern, indem wir die __next__()-Methode auf dem Python Iterator aufrufen. Sie sorgt dafür, den Pointer um ein Zeichen zu verschieben, sodass „iteratorobjekt“ nach dem Funktionsaufruf auf den ersten Buchstaben der Zeichenkette zeigt.
Der Funktionsaufruf von __iter__(), der den Python Iterator als Übergabeparameter entgegennimmt, liefert eine Referenz auf ebendiesen zurück. Python Iterators sind also selbst-iterierbar.
Iterators vs. Generators
Wichtig ist es auch, Iterators von Python Generators zu unterscheiden. Zwar ist jeder Generator in Python auch ein Iterator, umgekehrt gilt das aber nicht. Denn im Gegensatz zu einem Generator wird ein Iterator nicht zwingend mit einer Funktion gebildet, die einen yield-Ausdruck enthält.
Wofür und warum werden Python Iterators genutzt?
Das Haupteinsatzgebiet von Iterators ist natürlich das Iterieren selbst. Der große Vorteil von Iterators in Python ist es, dass sie nach dem Prinzip „Lazy Evaluation“ funktionieren. Anders gesagt bedeutet dies, dass jedes Element aus einem Python Iterator einzeln bearbeitet werden kann, ohne dass die Datenstruktur in Gänze in den Speicher geladen werden muss. Ein Effizienzvorteil ist dieses Verhalten vor allem bei großen Datenmengen, bei denen so zu jeder Zeit nur ein Element geladen werden muss.
So erstellen Sie Python Iterators
Einen eigenen Python Iterator zu erstellen, ist nicht schwierig. Sie müssen hierzu einem Python-Objekt lediglich die __iter__()- und die __next__()-Funktion hinzufügen. So können Sie ganz einfach einen Iterator erstellen, der alle geraden Zahlen zurückgibt. Das Codebeispiel zeigt, wie das genau funktioniert:
class geradeZahlen:
def __iter__(self):
self.x = 0
return self
def __next__(self):
a = self.x
self.x += 2
return a
testobjekt = geradeZahlen()
testiterator = iter(testobjekt)
print(next(testiterator))
print(next(testiterator))
print(next(testiterator))
print(next(testiterator))
Um einen Python Iterator zu verwirklichen, wird zunächst eine Klasse erstellt, die hier den Namen „geradeZahlen“ trägt. Innerhalb der Klasse werden die beiden Funktionen __iter__() und __next__() jeweils mit dem gewünschten Verhalten implementiert.
Die Funktion __iter__() gibt in unserem Falle einfach nur eine Referenz auf den Iterator zurück, der die Zahlenfolge der ganzen Zahlen beginnend bei der 0 beherbergen soll. Die Logik der Iteration, also dass nur jede zweite und somit jede gerade Zahl ausgegeben wird, befindet sich innerhalb der __next__()-Funktion.
Nachdem die Klasse definiert wurde, wird ein Objekt der Klasse erstellt und in der Variablen namens „testobjekt“ hinterlegt. Zu einem Python Iterator wird das Objekt durch den Aufruf der iter()-Funktion, wie wir im vorherigen Codebeispiel schon gezeigt haben. Anschließend folgen vier Aufrufe von next(), deren Ergebnisse auf dem Bildschirm ausgegeben werden. Der Output des obigen Codeabschnitts sieht wie folgt aus:
0
2
4
6
Python Iterators begrenzen
Der Iterator, der über die geraden Zahlen iteriert, würde ohne eine Begrenzung genau wie die Menge der geraden Zahlen ins Unendliche laufen. Dies kann durch ein „StopIteration“-Statement in Verbindung mit einem if-else-Statement in Python verhindert werden. Möchten Sie in Ihrem Python Iterator beispielsweise alle geraden Zahlen bis einschließlich der 100 ausgeben, müssen Sie das obige Codebeispiel wie folgt anpassen:
class geradeZahlen:
def __iter__(self):
self.x = 0
return self
def __next__(self):
if self.x <= 100:
a = self.x
self.x += 2
return a
else:
StopIteration
testobjekt = geradeZahlen()
testiterator = iter(testobjekt)
print(next(testiterator))
print(next(testiterator))
print(next(testiterator))
print(next(testiterator))
Im Code hat sich außer der Implementation __next__()-Funktion nichts verändert. Hier wurde ein zusätzliches if-else-Statement eingebaut, das überprüft, ob die aktuelle Zahl kleiner oder gleich 100 ist und nur unter dieser Bedingung weiter durch die Menge der geraden Zahlen iteriert. Wenn die Zahl den Wert 100 übersteigt, wird durch den Aufruf von StopIteration ein Fehler ausgelöst.