Historie
Python hat ab Version 2.4 die Listenverständnisse um sogenannte "Generatorausdrücke" erweitert. Sie ermöglichen die Erstellung einer faulen Wertesequenz, die den Generatoren ähnelt, jedoch in einer kompakten und lesbaren Form.
Problem
Listenverständnisse ([x for x in iterable]) erstellen eine Liste, wobei alle Elemente sofort in den Speicher geladen werden. Dies ist ineffizient oder sogar gefährlich, wenn die Anzahl der Elemente sehr groß ist. Generatorfunktionen (mit yield) sind flexibler, erfordern jedoch die separate Definition einer Funktion und mehr Codezeilen.
Lösung
Generatorausdrücke ((x for x in iterable)) bieten eine prägnante Syntax zur Erzeugung von faulen Sequenzen (Elemente werden nach Bedarf berechnet und nicht alle auf einmal geladen). Sie sehen ähnlich aus wie Listenverständnisse, verwenden jedoch runde Klammern:
# Listenverständnis lädt alles in den Speicher squares_list = [x**2 for x in range(10**6)] # Generatorausdruck: Elemente kommen nach Anfrage, der Speicher wird kaum genutzt squares_gen = (x**2 for x in range(10**6)) # Die ersten fünf Werte des Generators abrufen for _ in range(5): print(next(squares_gen))
Wesentliche Merkmale:
Kann man dasselbe Generatorausdruck mehrere Male "durchlaufen"?
Nein, nach einmaligem Iterieren ist der Generator "erschöpft". Um erneut zu iterieren, muss ein neuer Generator erstellt oder ein Listenverständnis verwendet werden.
it = (x for x in range(3)) print(list(it)) # [0,1,2] print(list(it)) # [] — keine Werte mehr verfügbar
Behalten Generatoren den Zustand zwischen den Verwendungen?
Ja, der Generatorausdruck behält die „Position“ zwischen den Aufrufen von next() (oder bei der nächsten Iteration) bei, kann jedoch nicht auf den Anfang zurückgesetzt werden, es sei denn, Sie erstellen ein neues Objekt.
Kann man ein Generatorausdruck in einer Zeile mehrfach verwenden?
Nein! Wenn Sie den Generator gleichzeitig an mehreren Stellen "entpacken" (z.B. in mehrere Funktionen, ohne ihn in eine Liste zurückzugeben), gehen Daten verloren — jede nachgelagerte Verwendung verschiebt den Zeiger nach vorne.
g = (x for x in range(3)) print(sum(g), list(g)) # sum(g) erhält alles, list(g) bleibt leer
Im Projekt zur Analyse großer Dateien wurde verwendet:
data = (parse_line(line) for line in file) process(list(data)) other_process(list(data))
Vorteile:
Nachteile:
Verwendet Listenverständnisse, wenn eine wiederholte Verwendung der Daten erforderlich ist, oder erstellt einen Generator für den einmaligen Verbrauch:
# Generator nur für einmalige Analyse (z.B. Summe berechnen) total = sum(parse_line(line) for line in file)
Vorteile:
Nachteile: