Tracks
/
Python
Python
/
Lehrplan
/
Generatoren
Ge

Generatoren in Python

1 Übung

Über Generatoren

Ein generator ist eine Funktion oder ein Ausdruck, der eine spezielle Art von Iterator zurückgibt, die Generator-Iterator genannt wird. Generator-iterators sind lazy: Sie speichern ihre values nicht im Arbeitsspeicher, sondern erzeugen ihre Werte, wenn sie gebraucht werden.

Eine Generator-Funktion sieht aus wie jede andere Funktion, enthält aber einen oder mehrere yield-Ausdrücke. Jedes yield unterbricht die Codeausführung und speichert den aktuellen Ausführungszustand (einschließlich aller lokalen Variablen und try-Anweisungen). Wenn der Generator fortgesetzt wird, nimmt er den Zustand von der Unterbrechung wieder auf, anders als reguläre Funktionen, die bei jedem Aufruf zurückgesetzt werden.

Einen Generator erstellen

Generatoren werden ähnlich aufgebaut wie andere Schleifen- oder rekursive Funktionen, benötigen aber einen yield-Ausdruck, den wir uns später noch genauer ansehen.

Ein Beispiel ist eine Funktion, die die Quadrate aus einer gegebenen Liste von Zahlen zurückgibt.
So wie sie jetzt geschrieben ist, muss die gesamte Eingabe verarbeitet werden, bevor Werte zurückgegeben werden können:

>>> def squares(list_of_numbers):
...     squares = []
...     for number in list_of_numbers:
...         squares.append(number ** 2)
...     return squares

Du kannst diese Funktion so in einen Generator umwandeln:

>>> def squares_generator(list_of_numbers):
...     for number in list_of_numbers:
...         yield number ** 2

Der Grund dafür ist, dass du einen Generator verwendest, wenn du nicht alle Werte auf einmal brauchst.

Das spart Arbeitsspeicher und Rechenleistung, da nur der Wert berechnet wird, an dem du gerade arbeitest.

Einen Generator verwenden

Generatoren (technisch gesehen generator-iterators, siehe den Hinweis unten) sind ein Typ von iterator und können überall in Python verwendet werden, wo ein iterator oder iterable erwartet wird. Dazu gehören Funktionen oder Objekte, die ein iterable/iterator als Argument benötigen. Für einen tieferen Einblick siehe How to Make an Iterator in Python.

Note

Generator-iteratoren sind eine spezielle Teilmenge von Iteratoren. Iterators sind der Mechanismus bzw. das Protokoll, das das Durchlaufen von Iterables ermöglicht. Generator-iteratoren und die Iteratoren, die gängige Python-iterables zurückgeben, verhalten sich sehr ähnlich, aber es gibt einige wichtige Unterschiede, die du beachten solltest:

  • Sie werden lazy ausgewertet; die Iteration ist einseitig und es gibt kein „Zurückspringen“ zu einem vorherigen Wert.
  • Sie werden verbraucht, wenn man über die zurückgegebenen Werte iteriert; es gibt kein Zurücksetzen und kein Speichern im Arbeitsspeicher.
  • Sie sind nicht sortierbar und können nicht umgekehrt werden.
  • Sie sind keine Sequenztypen und haben keine indexes. Du kannst nicht per Addition oder Subtraktion auf einen vorherigen oder zukünftigen Wert verweisen, und du kannst weder die Klammernotation ([]) noch Slicing verwenden.
  • Sie können nicht mit der Funktion len() verwendet werden, da sie keine Länge haben.
  • Sie können endlich oder unendlich sein. Sei vorsichtig, wenn du alle Werte aus einem unendlichen generator-iterator sammelst!

So verwendest du den Generator squares_generator():

>>> squared_numbers = squares_generator([1, 2, 3, 4])

>>> for square in squared_numbers:
...     print(square)
...
1
4
9
16

Werte innerhalb eines Generators können auch über die Funktion next() erzeugt bzw. abgerufen werden. next() ruft die Methode __next__() eines Generatorobjekts auf, „rückt“ den Generatorcode bis zu seinem yield-Ausdruck vor bzw. wertet ihn bis dahin aus, der dann den Wert „liefert“ bzw. zurückgibt.

>>> squared_numbers = squares_generator([1, 2])

>>> next(squared_numbers)
1
>>> next(squared_numbers)
4

Wenn ein generator vollständig verbraucht ist und keine weiteren Werte mehr zurückgibt, wirft er einen StopIteration-Fehler.

>>> next(squared_numbers)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

Unterschied zwischen Iterables und Generatoren

Generatoren sind eine spezielle Teilmenge von Iteratoren. Iterators sind der Mechanismus bzw. das Protokoll, das das Durchlaufen von Iterables ermöglicht. Generatoren und die Iteratoren, die gängige Python-iterables zurückgeben, verhalten sich sehr ähnlich, aber es gibt einige wichtige Unterschiede, die du beachten solltest:

  • Generatoren sind einseitig; es gibt kein „Zurückspringen“ zu einem vorherigen Wert.

  • Das Iterieren über Generatoren verbraucht die zurückgegebenen Werte; kein Zurücksetzen.

  • Generatoren (da sie lazy ausgewertet werden) sind nicht sortierbar und können nicht umgekehrt werden.

  • Generatoren haben keine indexes, du kannst also nicht per Addition oder Subtraktion auf einen früheren oder späteren Wert verweisen.

  • Generatoren können nicht mit der Funktion len() verwendet werden.

  • Generatoren können endlich oder unendlich sein. Sei vorsichtig, wenn du alle Werte aus einem unendlichen Generator sammelst.

Der yield-Ausdruck

Der yield-Ausdruck ist dem return-Ausdruck sehr ähnlich.

Im Unterschied zum return-Ausdruck gibt yield Werte an den Aufrufer an einem bestimmten Punkt ab und unterbricht die Auswertung bzw. Rückgabe weiterer Werte, bis diese angefordert werden.

Wenn yield ausgewertet wird, pausiert es die Ausführung der umgebenden Funktion und gibt die Werte der Funktion zu diesem Zeitpunkt zurück.

Die Funktion bleibt dann im Gültigkeitsbereich, und wenn __next__() aufgerufen wird, wird die Ausführung fortgesetzt, bis yield erneut auftritt.

Note

Die Verwendung von yield-Ausdrücken ist außerhalb von Funktionen nicht erlaubt.

>>> def infinite_sequence():
...     current_number = 0
...     while True:
...         yield current_number
...         current_number += 1

>>> lets_try = infinite_sequence()
>>> lets_try.__next__()
0
>>> lets_try.__next__()
1

Warum Generatoren?

Generatoren sind in vielen Anwendungen nützlich.

Wenn du mit einer großen Sammlung arbeitest, möchtest du vielleicht nicht alle ihre Werte in memory ablegen. Ein Generator kann verwendet werden, um größere Daten Stück für Stück zu verarbeiten, was Arbeitsspeicher spart und die Leistung verbessert.

Generatoren sind auch sehr hilfreich, wenn ein Prozess oder eine Berechnung komplex, teuer oder unendlich ist:

>>> def infinite_sequence():
...     current_number = 0
...     while True:
...         yield current_number
...         current_number += 1

Jetzt gibt das Objekt infinite_sequence jedes Mal, wenn __next__() dafür aufgerufen wird, die vorherige Zahl + 1 zurück.

Über GitHub bearbeiten Der Link öffnet sich in einem neuen Fenster oder Tab

Lerne Generatoren

Das Üben ist gesperrt

Schalte 2 weitere Übungen frei, um Generatoren zu üben