Bei Python Ge­ne­ra­tors handelt es sich um eine spezielle Art von Funk­tio­nen in Python. Sie erzeugen Werte Schritt für Schritt und er­mög­li­chen so spei­cher­ef­fi­zi­en­tes Arbeiten.

Was genau sind Python Ge­ne­ra­tors?

Python Ge­ne­ra­tors sind besondere Funk­tio­nen, die einen Python Iterator zu­rück­ge­ben. Die Art, wie Sie Python Ge­ne­ra­tors erstellen, ähnelt einer normalen Funk­ti­ons­de­fi­ni­ti­on. Der Un­ter­schied liegt im Detail: Statt einem return-Statement gibt es bei Ge­ne­ra­tors ein so­ge­nann­tes yield-Statement. Außerdem im­ple­men­tie­ren Ge­ne­ra­tor­funk­tio­nen wie Itera­ra­tors auch eine next()-Funktion.

Das Schlüs­sel­wort yield

Wenn Sie schon Erfahrung mit anderen Pro­gram­mier­spra­chen oder mit Python haben, dann kennen Sie das return-Statement. Dieses wird dazu genutzt, von Funk­tio­nen be­rech­ne­te Werte an die auf­ru­fen­de Instanz im Pro­gramm­code wei­ter­zu­ge­ben. Nachdem das return-Statement einer Funktion erreicht wurde, wird die Funktion verlassen und ihre Aus­füh­rung ist beendet. Bei Bedarf kann die Funktion einfach wieder auf­ge­ru­fen werden.

Mit yield verhält es sich anders: Das Schlüs­sel­wort ersetzt das return-Statement in Python Ge­ne­ra­tors. Wenn Sie Ihren Generator nun aufrufen, wird der Wert zu­rück­ge­ge­ben, den Sie dem yield-Statement übergeben. Danach wird ein Python Generator al­ler­dings nicht verlassen, sondern lediglich un­ter­bro­chen. Der aktuelle Status der Ge­ne­ra­tor­funk­ti­on wird quasi ge­spei­chert. Bei einem erneuten Funk­ti­ons­auf­ruf Ihres Ge­ne­ra­tors springen Sie dann an die ge­spei­cher­te Stelle.

Ein­satz­ge­bie­te von Python Ge­ne­ra­tors

Aufgrund der Tatsache, dass Python Ge­ne­ra­tors nach dem Prinzip „Lazy Eva­lua­ti­on“ verfahren und Werte immer erst dann auswerten, wenn sie tat­säch­lich benötigt werden, eignen sich Ge­ne­ra­tor­funk­tio­nen her­vor­ra­gend für die Arbeit mit sehr großen Da­ten­men­gen.

Eine normale Funktion würde den gesamten Da­tei­in­halt zunächst in eine Variable und somit in Ihren Speicher laden. Bei großen Da­ten­men­gen könnte Ihr lokaler Speicher unter Umständen nicht aus­rei­chen und der Vorgang würde zu einem Me­mo­ry­Er­ror führen. Mit Ge­ne­ra­to­ren lassen sich derartige Probleme leicht umgehen, indem die Datei Zeile für Zeile aus­ge­le­sen wird. Das Schlüs­sel­wort yield gibt Ihnen den Wert zurück, den Sie aktuell benötigen, und un­ter­bricht dann die Aus­füh­rung der Funktion bis zum nächsten Funk­ti­ons­auf­ruf, der eine weitere Zeile der Datei be­ar­bei­tet.

Tipp

Viele Web­an­wen­dun­gen erfordern die Ver­ar­bei­tung großer Da­ten­men­gen. Python eignet sich auch für den Einsatz in Web­pro­jek­ten. Mit Deploy Now können Sie das Erstellen Ihrer Web­pro­jek­te be­schleu­ni­gen, indem Sie von au­to­ma­ti­schem De­ploy­ment und Building via GitHub pro­fi­tie­ren.

Doch nicht nur das Handling großer Da­ten­men­gen, sondern auch die Arbeit mit Un­end­lich­keit wird durch Python Ge­ne­ra­tors enorm er­leich­tert. Da lokaler Speicher endlich ist, sind Ge­ne­ra­tors die einzige Mög­lich­keit, un­end­li­che Listen oder ähnliches in Python zu erzeugen.

CSV-Dateien mit Python Ge­ne­ra­tors auslesen

Wie erwähnt eignen sich Ge­ne­ra­tors vor allem für die Arbeit mit großen Da­ten­men­gen. Das folgende Programm er­mög­licht es, eine CSV-Datei spei­cher­ef­fi­zi­ent Zeile für Zeile aus­zu­le­sen:

import csv
def csv_lesen(dateiname):
	with open(dateiname, 'r') as datei:
		tmp = csv.reader(datei)
		for zeile in tmp:
			yield zeile
for zeile in csv_lesen('test.csv'):
	print(zeile)

Im Code­bei­spiel im­por­tie­ren wir zunächst das Modul csv, um Zugriff auf die Python-Funk­tio­nen zum Ver­ar­bei­ten von CSV-Dateien zu haben. An­schlie­ßend sehen Sie die De­fi­ni­ti­on eines Python Ge­ne­ra­tors namens „csv_lesen“, die wie Funk­ti­ons­de­fi­ni­tio­nen mit dem Schlüs­sel­wort „def“ beginnt. Nachdem die Datei geöffnet wurde, wird in die Python-for-Loop Zeile für Zeile durch die Datei iteriert. Jede Zeile wird dabei mit dem Schlüs­sel­wort „yield“ zu­rück­ge­ge­ben.

Außerhalb der Ge­ne­ra­tor­funk­ti­on werden die Zeilen, die der Python Generator zu­rück­gibt, nach­ein­an­der auf der Konsole aus­ge­ge­ben. Hierfür wird die Python-print-Funktion genutzt.

Un­end­li­che Da­ten­struk­tu­ren mit Python Ge­ne­ra­tors erstellen

Eine un­end­li­che Da­ten­struk­tur kann lo­gi­scher­wei­se nicht lokal auf Ihrem Rechner ge­spei­chert werden. Al­ler­dings sind un­end­li­che Da­ten­struk­tu­ren für einige An­wen­dun­gen es­sen­zi­ell. Auch hier helfen Ge­ne­ra­tor­funk­tio­nen weiter, da sie alle Elemente nach­ein­an­der be­ar­bei­ten und so den Speicher nicht über­flu­ten. Ein Beispiel für eine un­end­li­che Folge na­tür­li­cher Zahlen könnte im Python-Code wie folgt aussehen:

def natuerliche_zahlen():
	n = 0
	while True:
		yield n
		n += 1
for zahl in nateurlilche_zahlen():
	print(zahl)

Zunächst wird ein Python Generator namens „na­tuer­li­che_zahlen“ definiert, der den Startwert der Variablen „n“ festlegt. An­schlie­ßend wird eine Python-while-Schleife gestartet, die endlos läuft. Mit „yield“ wird der aktuelle Wert der Variablen zu­rück­ge­ge­ben und die Aus­füh­rung der Ge­ne­ra­tor­funk­ti­on un­ter­bro­chen. Wenn die Funktion ein weiteres Mal auf­ge­ru­fen wird, wird die zuvor aus­ge­ge­be­ne Zahl um 1 in­kre­men­tiert und der Generator wieder so lange durch­lau­fen, bis der In­ter­pre­ter auf das Schlüs­sel­wort „yield“ trifft. In der for-Schleife unterhalb der Ge­ne­ra­tor­funk­ti­on werden die vom Generator erzeugten Zahlen aus­ge­ge­ben. Wird das Programm nicht manuell un­ter­bro­chen, läuft es endlos.

Kurz­schreib­wei­se für Python Ge­ne­ra­tors

Mit List Com­pre­hen­si­ons können Sie Python-Listen in nur einer Zeile Code erstellen. Eine ähnliche Kurz­schreib­wei­se gibt es auch für Ge­ne­ra­to­ren. Schauen wir uns einen Generator an, der die Zahlen von 0 bis 9 jeweils um den Wert 1 in­kre­men­tiert. Er ähnelt dem Generator, den wir für die Ge­ne­rie­rung der un­end­li­chen Folge von na­tür­li­chen Zahlen genutzt haben.

def natuerliche_zahlen():
	n = 0
	while n <= 9:
		yield n
		n+=1

Wenn Sie diesen Generator in einer Zeile Code schreiben möchten, nutzen Sie ein for-Statement in runden Klammern wie in folgendem Beispiel:

increment_generator = (n + 1 for n in range(10))

Wenn Sie diesen Generator nun ausgeben möchten, erhalten Sie folgende Ausgabe:

<generator object <genexpr> at 0x0000020CC5A2D6C8>

Ihnen wird also angezeigt, wo in Ihrem Speicher sich das erstellte Generator-Objekt befindet. Um auf die Ausgabe Ihres Ge­ne­ra­tors zu­zu­grei­fen, bietet sich die next()-Funktion an:

print(next(increment_generator))
print(next(increment_generator))
print(next(increment_generator))

Dieser Code­ab­schnitt liefert folgenden Output, bei dem die Zahlen von 0 bis 2 jeweils um 1 in­kre­men­tiert wurden:

1
2
3

Ge­ne­ra­tors vs. List Com­pre­hen­si­ons

Die Kurz­schreib­wei­se von Ge­ne­ra­to­ren erinnert stark an List Com­pre­hen­si­ons. Der einzige visuelle Un­ter­schied liegt in der Klam­me­rung: Während Sie bei Com­pre­hen­si­ons auf eckige Klammern setzen, nutzen Sie für die Er­stel­lung von Python Ge­ne­ra­tors runde Klammern. Doch intern gibt es einen viel we­sent­li­che­ren Un­ter­schied: Der Spei­cher­be­darf von Ge­ne­ra­tors ist viel geringer als der von Listen.

import sys
increment_liste = [n + 1 for n in range(100)]
increment_generator = (n + 1 for n in range(100))
print(sys.getsizeof(increment_liste))
print(sys.getsizeof(increment_generator))

Das obige Programm gibt den Spei­cher­be­darf der Liste und des äqui­va­len­ten Ge­ne­ra­tors aus:

912
120

Während die Liste 912 Bytes Spei­cher­platz benötigt, kommt der Generator mit gerade einmal 120 Bytes aus. Dieser Un­ter­schied wird noch immenser, wenn die zu ver­ar­bei­ten­de Da­ten­men­ge steigt.

Zum Hauptmenü