python yield

Python Yield – Lösung für große Datenmengen

Python yield ist eine Technik, mit der sich große Datenmengen in kleineren Schritten abarbeiten lassen. Wir erklären, wie Sie das Schlüsselwort yield in Python benutzen und warum dabei das Konzept des Generators wichtig ist.

Hintergund und Anwendungsfälle

Frühere Programmierer mussten möglichst sparsam mit den Ressourcen umgehen – die Hardware bot viele Restriktionen, Speicher zum Beispiel war knapp. Heutige Hardware ist glücklicherweise deutlich leistungsfähiger, aber speziell der Hauptspeicher ist auch heute noch endlich.

Dies kann beim Bearbeiten großer Datenmengen Probleme bereiten, da sich diese nicht in einem Stück in den Hauptspeicher laden lassen. Python yield ist dafür eine Lösungsmöglichkeit.

Große Datenmengen, die die Benutzung von Python yield interessant machen oder gar erfordern, sind meist in folgende drei Kategorien einteilbar:

  • Große Dateien wie CSV-Listen einlesen
  • Infiniter Zahlenreihen oder Listen generieren
  • Vergleichsoperationen vornehmen, bei denen der Algorithmus große Working Sets generiert

Was ist Python yield?

Was aber genau bedeutet nun yield in Python? Vereinfacht gesagt, hat das Schlüsselwort yield in Python die gleiche Aufgabe wie das Schlüsselwort return – beide geben einen Wert an die aufrufende Funktion zurück.

Im Gegensatz zu return sparen Programmierende mit yield jedoch Ressourcen, weil immer nur ein Wert zurückgegeben und nicht beispielsweise alle Werte einer kompletten Schleife erzeugt werden.

Um yield im Detail zu verstehen, müssen Sie zunächst das Konzept eines Iterables kennen und dann verstehen, was ein Generator macht. Beides erklären die folgenden Abschnitte.

Konzept des Iterables

Die meisten Programmiersprachen verfügen über das Werkzeug einer Schleife; diese wird solange durchlaufen und wiederholt, bis das gewünschte Ergebnis erreicht ist. Beispielsweise kann man so Listen generieren. Im Englischen werden Mechanismen wie Schleifendurchläufe Iteration genannt, entsprechende Funktionen sind iterable.

Natürlich kennt auch Python das Konzept bzw. den Begriff des „iterable“. Im folgenden Beispiel nutzen wir eine einfache Funktion iterativ, um Daten zu erzeugen.

meineliste = [x*x for x in range(5)]
for i in meineliste:
print(i)

Hier im Beispiel werden nur 5 Zahlen generiert, aber in der Praxis können Datensätze sehr viel größer sein. Das Problem dann: Die Zahlen werden alle permanent im Hauptspeicher gehalten, solange das Programm ausgeführt wird, und belegen entsprechenden Platz.

Python Yield - Beispielcode
Python Yield – Beispielcode

Generatoren als Bindeglied zwischen Iterable und Python yield

Wenn die Datenmengen einer solchen iterativen Funktion sehr groß sind, ist es sinnvoll, nicht alles auf einmal zu generieren, den Speicher vollzuschreiben und den PC damit schlimmstenfalls lahmzulegen. (Falls es doch passiert, hilft der Tipp: Programm: Schließen erzwingen)

Ein Ausweg aus dem Dilemma bieten sogenannte Generatoren. Ein Generator iteriert nur einmal, durchläuft also zum Beispiel eine Schleife nur einmal pro Aufruf. Er bestimmt den Wert also erst bei Aufruf der entsprechenden Position und stellt diesen zur Verfügung. Auf diese Weise belegt man keinen (oder besser gesagt: wenig) wertvollen Hauptspeicher!

Python Yield

Und hier kommt das Schlüsselwort yield ins Spiel. Denn dieses macht eine „normale“ Funktion zum Generator. Sobald der Computer beim Ausführen des Codes auf yield trifft, gibt er den ersten Wert zurück. Bei einem weiteren Aufruf durchläuft er die Schleife ein weiteres Mal und liefert den nächsten Wert und so fort.

Ein Generator wird also im Grunde nur dadurch zum Generator, indem Sie das Schlüsselwort yield statt return verwenden.

Beispiel zu Python yield: Arbeiten mit großen Datensätzen

Unser folgendes Beispiel soll das verdeutlichen.

Eine Aufgabenstellung kann darin bestehen, in einer großen CSV-Datei die Anzahl der Zeilen zu zählen.

csv_gen = csv_reader("Temperaturen.txt")
row_count = 0

for row in csv_gen:
row_count += 1

print(f"Row count is {row_count}")

In diesem Beispiel laden wir eine Datei „Temperaturen.txt“ und lassen die Zeilenanzahl zählen. Wenn die Ladefunktion einfach gestaltet ist, öffnet das Programm die Datei schlicht und einfach mit einem open-Aufruf und liest diese ein, so wie hier:

def csv_reader(file_name):

file = open(file_name)
result = file.read().split("\n")

return result

Hiermit wird die Datei geöffnet, eingelesen und mit „file.read.split“ in einzelne Zeilen unterteilt, die anschließend in einem Python Listenelement abgelegt werden. Dies verbraucht Hauptspeicher, der speziell in kompakten Systeme wie einem Raspberry Pi oder ähnlichen embedded-Computern nur begrenzt vorhanden ist.

Irgendwann im Laufe des Einlesevorgangs wird das System drastisch langsamer und irgendwann einen MemoryError melden.

Beispiel mit Python Yield

Anstelle der obigen Variante lässt sich die Einlesefunktion aber auch anders gestalten, zum Beispiel so:

def csv_reader(file_name):

for row in open(file_name, "r"):
yield row

Mit dem Schlüsselwort yield haben wir den aufgerufenen Ausdruck csv_reader() in eine Generatorfunktion umgewandelt. Ohne yield würde Python die Schleife so oft ausführen, wie Zeilen in der CSV-Datei sind, und diese in einem Listenelement ablegen.

Dabei würden alle Daten im Hauptspeicher vorhanden sein müssen.

Python yield sorgt somit dafür, dass die Datei geöffnet wird, durch jede Zeile durchgegangen wird, aber die Werte nicht sofort zurückgegeben werden an die aufrufende Funktion.

Fazit

Python yield hilft, Ressourcen zu sparen und effizient zu programmieren. Ihr Einsatz erfordert vom Programmierer das Verständnis des Zusammenspiels zwischen Schleifen, iterativer Abarbeitung und schrittweisem Handling der Rückgabewerte. Yield maskiert letztere sozusagen und hält sie nicht en bloc im Hauptspeicher.

Weiterführende Links

Yield in der offiziellen Dokumentation (en)



FavoriteLoading Tipp meinen Favoriten hinzufügen (setzt Cookie, mehr unter Datenschutz)