Menu

Python-Generatoren: yield, Lazy-Iteration und Generator-Ausdrücke

Wie Generatoren in Python Werte lazy erzeugen — das Schlüsselwort yield, Generator-Ausdrücke und wann sie eine schlichte Liste schlagen.

Eine Funktion, die pausiert

Ein Generator sieht aus wie eine normale Funktion, aber statt ein ganzes Ergebnis zu berechnen und zurückzugeben, liefert er einen Wert nach dem anderen und pausiert zwischen den Yields, bis der Konsument den nächsten Wert will.

Der einfachste mögliche:

main.py
Output
Click Run to see the output here.

Beachte yield statt return. Das erste Mal, wenn for einen Wert will, läuft Python den Funktionsrumpf bis yield 1. Die Funktion pausiert genau dort, reicht 1 an die Schleife und merkt sich, wo sie angehalten hat — Variablen und alles. Die nächste Iteration setzt dort an: current += 1, zurück zum while, yield 2. Und so weiter, bis die Schleifenbedingung scheitert; dann hält der Generator einfach an.

Dieses Anhalten-und-Weitermachen ist der ganze Trick.

Warum nicht einfach eine Liste bauen?

Weil die Listenversion alle Werte im Voraus alloziert:

main.py
Output
Click Run to see the output here.

Okay für 5 Elemente. Jetzt stell dir vor, du willst 50 Millionen Ganzzahlen und interessierst dich nur für die erste, die eine Bedingung erfüllt. Die Listenversion alloziert 50 Millionen Ints und wirft die meisten weg. Die Generator-Version erzeugt genau so viele, wie die Aufruferin konsumiert. Findet die for-Schleife, was sie will, und bricht ab, hält der Generator einfach an.

Das Muster, das du verinnerlichen solltest: mit Generatoren schreibst du Iterationscode, ohne vorher zu entscheiden, wie viel vom Ergebnis du brauchst.

Generator-Ausdrücke

Wenn du eine List Comprehension geschrieben hast, kennst du die Syntax schon — tausch die eckigen gegen runde Klammern:

main.py
Output
Click Run to see the output here.

squares_gen berechnet noch nichts. Es ist nur ein Rezept. Iteration führt das Rezept Schritt für Schritt aus.

Generator-Ausdrücke sind ideal als Argumente für Funktionen, die eine Iterable konsumieren:

main.py
Output
Click Run to see the output here.

Keine Zwischenliste. sum, max und any lesen Werte einzeln, genau das, was sie wollen.

Eine große Datei Zeile für Zeile lesen

Das ist der kanonische Praxisfall für Generatoren — eine Datei verarbeiten, die zu groß für den Speicher ist:

def parse_log_lines(path):
    with open(path) as f:
        for line in f:
            if line.startswith("ERROR"):
                yield line.rstrip()

for error in parse_log_lines("app.log"):
    print(error)

Die Datei wird lazy gelesen. Jeder Aufruf des Generators holt eine Zeile von der Platte, filtert sie und gibt sie aus. Der Speicherverbrauch bleibt flach, unabhängig von der Dateigröße.

Einmal und fertig

Ein Generator hat einen einzigen Durchgang. Nach der Iteration bis zum Ende ist er erschöpft:

main.py
Output
Click Run to see the output here.

Die zweite Schleife gibt nichts aus. Der Generator hat nichts mehr.

Musst du mehrfach iterieren, ruf die Generator-Funktion erneut auf oder materialisier die Sequenz mit list(...) und iterier die Liste. Entscheide nach Kosten: neu bauen ist okay, wenn die Arbeit günstig ist; eine Liste ist okay, wenn die Sequenz klein ist.

next() und manuelle Iteration

Du musst keine for-Schleife nutzen. next() zieht einen Wert nach dem anderen:

main.py
Output
Click Run to see the output here.

StopIteration ist, wie ein Generator „ich bin fertig“ signalisiert. for-Schleifen fangen es still ab. Im manuellen Code kannst du next(gen, default) übergeben, um die Ausnahme zu vermeiden.

Unendliche Generatoren

Weil Werte auf Abruf erzeugt werden, kann ein Generator eine Sequenz ohne Ende darstellen — solange der Konsument aufhört zu fragen:

main.py
Output
Click Run to see the output here.

while True mit einem yield drin hängt das Programm nicht auf — es heißt nur „wenn jemand weiter fragt, liefere weiter“. Der Konsument entscheidet, wann Schluss ist.

Das Muster taucht bei Streaming-Daten, Event-Loops und überall da auf, wo du Werte aus einer Quelle ohne definierte Länge ziehst.

yield from: an eine andere Iterable delegieren

Will dein Generator jeden Wert aus einer anderen Iterable liefern, macht yield from das in einer Zeile:

main.py
Output
Click Run to see the output here.

Ohne yield from würdest du eine verschachtelte for-Schleife mit yield x schreiben. Es reicht auch send()- und throw()-Aufrufe korrekt weiter, falls du die je nutzt — aber für alltäglichen Code: denk dran als „liefere jeden Wert aus diesem Ding“.

Wann du zu einem Generator greifst

Drei Signale, dass ein Generator das richtige Werkzeug ist:

  1. Die Sequenz ist groß, möglicherweise unendlich oder teuer in voller Form zu erzeugen.
  2. Die Konsumentin könnte vor dem Ende stoppen (ein break beim ersten Treffer etwa).
  3. Du willst Transformationen verketten — filtern, mappen, abschneiden —, ohne Zwischenlisten zu bauen.

Und wann nicht:

  • Du brauchst Zufallszugriff (seq[42]). Generatoren gehen nur vorwärts.
  • Du musst dieselbe Sequenz mehrfach iterieren. Nimm eine Liste.
  • Die Sequenz ist klein und du hast sie schon. Eine List Comprehension ist einfacher.

Generatoren, List Comprehensions und schlichte Listen sind für verschiedene Jobs jeweils die richtige Antwort. Die Fertigkeit ist, ohne viel Nachdenken zu wählen — und der schnellste Weg, diesen Instinkt zu entwickeln, ist: bei jeder Iteration, die du schreibst, bemerken, ob „erst alles produzieren“ oder „einzeln produzieren“ besser passt.

Als Nächstes: Context Manager im Detail

Du hast jetzt die meisten Idiome gesehen, die Python für Iteration nutzt. Context Manager — die with-Anweisung — kommen als Nächstes und passen gut zu Generatoren, um Daten aus Dateien und Netzwerkverbindungen zu streamen.

Häufig gestellte Fragen

Was ist ein Generator in Python?

Ein Generator ist eine Funktion, die Werte einzeln produziert und dazwischen pausiert. Du schreibst ihn mit def wie eine normale Funktion, nutzt aber yield statt return. Der Aufruf liefert ein Generator-Objekt; jede Iteration eines for oder jeder next()-Aufruf läuft die Funktion bis zum nächsten yield.

Was ist der Unterschied zwischen einer Liste und einem Generator?

Eine Liste hält jedes Element gleichzeitig im Speicher. Ein Generator berechnet Elemente auf Abruf und vergisst sie nach dem Konsum. Für große oder unendliche Sequenzen nutzt ein Generator einen winzigen festen Speicherbedarf; für kleine Ergebnisse, die du mehrfach brauchst, ist eine Liste besser.

Kann ich einen Generator zweimal iterieren?

Nein. Ein Generator ist nach dem ersten Durchgang erschöpft — eine zweite for-Schleife darüber liefert nichts. Brauchst du mehr als einmal, ruf die Generator-Funktion erneut auf, um einen frischen Generator zu bekommen, oder materialisier die Ergebnisse in einer Liste.

Lerne mit Coddy zu programmieren

LOS GEHT'S