Generatoren in Python sind ein mächtiges Werkzeug für die effiziente Arbeit mit Datenfolgen. Sie ermöglichen die Erstellung von iterierbaren Objekten, die Werte nur bei Bedarf berechnen und so den Systemspeicher erheblich schonen. Die Grundlage von Generatoren ist das Schlüsselwort yield, das sich grundlegend von der traditionellen return-Anweisung unterscheidet.
Grundlagen der Python-Generatoren
Generatoren sind spezielle Funktionen, die anstelle konkreter Werte einen Iterator zurückgeben. Der Hauptunterschied liegt in der Verwendung des Schlüsselworts yield, das den Zustand der Funktion zwischen den Aufrufen speichert.
Bei jedem Aufruf der Generatorfunktion wird die Ausführung an der Stelle fortgesetzt, an der die letzte yield-Anweisung gestoppt wurde. Das macht Generatoren zur idealen Lösung für die Verarbeitung großer Datenströme, ohne dass diese vollständig im Speicher abgelegt werden müssen.
def einfacher_generator():
yield 1
yield 2
yield 3
gen = einfacher_generator()
for wert in gen:
print(wert)
Ausgabe:
1
2
3
So funktioniert das Schlüsselwort yield
Das Schlüsselwort yield arbeitet nach dem Prinzip der "lazy evaluation" (faulen Berechnung):
- Beim ersten Aufruf von
next()beginnt die Ausführung am Anfang der Funktion bis zum erstenyield - Nach der Rückgabe des Werts "friert" die Funktion ihren Zustand ein
- Der nächste Aufruf von
next()setzt die Ausführung an der Stoppstelle fort - Dieser Prozess wird bis zum Ende der Funktion fortgesetzt
def zaehler():
print("Generator gestartet")
yield 1
print("Nach dem ersten yield")
yield 2
print("Nach dem zweiten yield")
gen = zaehler()
next(gen) # Ausgabe: Generator gestartet
next(gen) # Ausgabe: Nach dem ersten yield
Vorteile von Generatoren gegenüber Listen
Die Verwendung von Generatoren anstelle von Listen bietet erhebliche Vorteile bei der Arbeit mit großen Datenmengen:
Speicherschonend: Generatoren speichern nicht alle Werte gleichzeitig, sondern erzeugen sie bei Bedarf.
# Liste - benötigt viel Speicher
quadrate_liste = [x * x for x in range(1000000)]
# Generator - minimaler Speicherverbrauch
quadrate_gen = (x * x for x in range(1000000))
Performance: Generatoren liefern sofort Werte zurück, ohne auf die Erstellung der gesamten Sequenz warten zu müssen.
Erstellung unendlicher Sequenzen
Mit Generatoren lassen sich unendliche Sequenzen erstellen, ohne dass ein Speicherüberlauf riskiert wird:
def unendlicher_zaehler(start=0):
while True:
yield start
start += 1
zaehler = unendlicher_zaehler()
print(next(zaehler)) # 0
print(next(zaehler)) # 1
print(next(zaehler)) # 2
Methoden zur Arbeit mit Generatoren
Generatoren unterstützen mehrere spezialisierte Methoden:
next() — Holt den nächsten Wert aus dem Generator send(value) — Sendet einen Wert in den Generator throw() — Löst eine Ausnahme innerhalb des Generators aus close() — Stoppt den Generator zwangsweise
def interaktiver_generator():
wert = yield "Start"
while True:
wert = yield f"Erhalten: {wert}"
gen = interaktiver_generator()
print(next(gen)) # Start
print(gen.send(42)) # Erhalten: 42
print(gen.send("Hallo")) # Erhalten: Hallo
Ausnahmebehandlung in Generatoren
Generatoren unterstützen die Ausnahmebehandlung, einschließlich der speziellen Ausnahme GeneratorExit:
def beispiel_generator():
try:
yield 1
yield 2
except GeneratorExit:
print("Generator geschlossen!")
gen = beispiel_generator()
print(next(gen)) # 1
gen.close() # Ausgabe: Generator geschlossen!