5.1 Iterable und Iterator
Wie du schon weißt, sind Iteratoren Objekte, die das Iterator-Protokoll implementieren und somit nacheinander Elemente aus einer Sammlung abrufen können. Iteratoren werden in Python häufig verwendet, um Elemente von Sequenzen wie Listen, Tupeln und Strings zu durchlaufen.
Schauen wir uns an, wie Iteratoren aufgebaut sind und wie man sie verwendet.
Iterierbares Objekt (Iterable)
Damit ein Objekt in einer for-Schleife durchlaufen werden kann, muss es iterierbar – Iterable – sein. Das bedeutet, dass unser Objekt die Methode __iter__() implementieren muss, die ein Iterator-Objekt zurückgibt.
Iterator-Objekt (Iterator)
Dies ist ein spezielles Objekt, das die Funktion __next__() hat, um das nächste Element der Sequenz zurückzugeben. Wenn keine Elemente mehr vorhanden sind, löst die Methode __next__() die Ausnahme StopIteration als Zeichen zum Stoppen der Iteration aus.
Der Iterator muss auch die Methode __iter__() implementieren, die den Iterator selbst zurückgibt.
Beispiel mit eingebauten Python-Funktionen
In diesem Beispiel ist die Liste numbers ein iterierbares Objekt. Wir erhalten den Iterator mit der Funktion iter() und nutzen die Funktion next(), um die Elemente so lange zu durchlaufen, bis die Ausnahme StopIteration ausgelöst wird.
# Iterierbares Objekt
numbers = [1, 2, 3, 4, 5]
# Erhalten des Iterators aus dem iterierbaren Objekt
iterator = iter(numbers)
# Verwendung des Iterators zum Durchlaufen der Elemente
try:
while True:
number = next(iterator)
print(number)
except StopIteration:
pass
Genau das passiert, wenn du Code dieser Art schreibst:
# Iterierbares Objekt
numbers = [1, 2, 3, 4, 5]
for number in numbers:
print(number)
5.2 Das Wesen des Iterators
Ein Iterator ist ein Objekt, das uns hilft, eine Gruppe von Elementen nacheinander zu durchlaufen. Die Implementierungen können sehr unterschiedlich sein. Lass uns eine eigene Klasse schreiben, die alle Anforderungen umsetzt, die an einen Iterator gestellt werden.
Schritt 1. Zuerst erstellen wir unsere eigene Klasse
Lass ihn der Reihe nach Zahlen von start bis end zurückgeben
class MyIterator:
def __init__(self, start, end):
self.current = start
self.end = end
Schritt 2. Unterstützung der Funktion __iter__
Jetzt müssen wir ihm die Funktion __iter__ hinzufügen, die das Iterator-Objekt zurückgibt, bei dem die Funktion __next()__ aufgerufen wird. Wir werden eine Referenz auf unser eigenes Objekt zurückgeben – das ist nicht verboten.
class MyIterator:
def __init__(self, start, end):
self.current = start
self.end = end
def __iter__(self):
return self
Schritt 3. Unterstützung der Funktion __next__
Jetzt müssen wir unserem Objekt-Iterator die Funktion __next__ hinzufügen, die das nächste Element unserer Liste zurückgeben wird. Wir werden einfach die Variable current verwenden:
def __next__(self):
current = self.current
self.current += 1
return current
Schritt 4. Stoppen des Iterators
Wenn der Iterator bereits alle geplanten Werte zurückgegeben hat, muss er die Ausnahme StopIteration auslösen. Lass uns unsere letzte Funktion ein wenig anpassen:
def __next__(self):
if self.current >= self.end: raise StopIteration
current = self.current
self.current += 1
return current
Super. Jetzt können wir unseren Iterator verwenden. Hier ist ein Beispiel für unseren gesamten Code:
class MyIterator:
def __init__(self, start, end):
self.current = start
self.end = end
def __iter__(self):
return self
def __next__(self):
if self.current >= self.end:
raise StopIteration
current = self.current
self.current += 1
return current
# Erstellen einer Instanz des benutzerdefinierten Iterators
my_iter = MyIterator(1, 5)
# Verwendung des Iterators zum Durchlaufen der Elemente
for num in my_iter:
print(num)
5.3 Der richtige Iterator
Warum ist der Iterator aus dem vorherigen Beispiel nicht ideal? Ja, er ist ein Iterator und funktioniert, aber er ist zu primitiv. Man kann nicht gleichzeitig mit mehreren Iteratoren durch dieselbe Sammlung von Elementen gehen.
Es wäre richtiger, Code zu schreiben, der im __iter__-Methode nicht eine Referenz auf sich selbst, sondern ein separates Objekt zurückgibt, das die Elemente korrekt ausgibt.
Beispiel:
class MyIterable:
def __init__(self, data):
self.data = data
def __iter__(self):
return MyIterator(self.data)
class MyIterator:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.data):
raise StopIteration
item = self.data[self.index]
self.index += 1
return item
# Nutzung
my_iterable = MyIterable([1, 2, 3, 4])
for item in my_iterable:
print(item)
In diesem Beispiel haben wir zwei Klassen — in die erste wird die Sammlung übergeben, durch die wir mit dem Iterator gehen werden. Die zweite ist der Iterator, der die Elemente der Sammlung in der Methode next() zurückgibt. Er ist auch ziemlich einfach, aber genau so sollte man Iteratoren in seine Klassen einfügen.
GO TO FULL VERSION