Kurz: Hinter jeder for-Schleife steckt ein Iterator: ein Objekt, das Element für Element herausgibt. Mit Generatoren schreibst du solche Objekte besonders einfach, und zwar mit dem Schlüsselwort yield.
Teil des Kurses Python
Hinter jeder for-Schleife steckt ein Iterator: ein Objekt, das Element für Element herausgibt. Mit Generatoren schreibst du solche Objekte besonders einfach, und zwar mit dem Schlüsselwort yield.
Wie for wirklich funktioniert
Listen, Texte, Dictionaries und andere Sammlungen sind iterierbar. Mit iter() bekommst du einen Iterator, mit next() das jeweils nächste Element. Ist nichts mehr da, entsteht StopIteration:
it = iter(["a", "b", "c"])
print(next(it))
print(next(it))
print(next(it))
print(next(it, "Ende")) # mit Standardwert statt FehlerAusgabe:
a
b
c
EndeEine Funktion mit yield
Eine Funktion mit yield heißt Generatorfunktion. Beim Aufruf läuft sie nicht sofort los, sondern liefert einen Generator. Bei jedem next() läuft sie bis zum nächsten yield und pausiert dort:
def zaehle_bis(n):
i = 1
while i <= n:
yield i
i += 1
for zahl in zaehle_bis(4):
print(zahl)
gen = zaehle_bis(2)
print(next(gen), next(gen))Ausgabe:
1
2
3
4
1 2Der Vorteil: Die Werte entstehen erst, wenn sie gebraucht werden. Ein Generator braucht fast keinen Speicher, auch wenn er Millionen Werte liefert.
Beispiel: Fibonacci-Zahlen
def fibonacci():
a, b = 0, 1
while True: # unendlich! – aber nur bei Bedarf berechnet
yield a
a, b = b, a + b
from itertools import islice
print(list(islice(fibonacci(), 10)))Ausgabe:
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]itertools.islice nimmt die ersten Elemente eines (auch unendlichen) Iterators.
Generator-Ausdrücke
Mit runden Klammern statt eckigen entsteht ein Generator ohne eigene Funktion:
quadrate = (x * x for x in range(1, 6))
print(next(quadrate))
print(sum(quadrate)) # summiert die restlichen vierAusgabe:
1
54Ein Generator kann man nur einmal durchlaufen:
g = (x for x in [1, 2, 3])
print(list(g))
print(list(g))Ausgabe:
[1, 2, 3]
[]Eigene Iteratoren
Eine Klasse wird zum Iterator, wenn sie __iter__ und __next__ besitzt. In den meisten Fällen ist ein Generator aber die einfachere Lösung:
class Countdown:
def __init__(self, start):
self.start = start
def __iter__(self):
n = self.start
while n > 0:
yield n
n -= 1
print(list(Countdown(3)))Ausgabe:
[3, 2, 1]Nützliche Werkzeuge aus itertools
| Funktion | Wirkung |
|---|---|
count(start) | unendlich zählen |
cycle(seq) | Folge endlos wiederholen |
islice(it, n) | die ersten n Elemente |
chain(a, b) | mehrere Folgen hintereinander |
product(a, b) | alle Kombinationen (kartesisches Produkt) |
permutations(seq) | alle Anordnungen |
combinations(seq, k) | alle Auswahlen ohne Reihenfolge |
from itertools import chain, combinations, product
print(list(chain([1, 2], "ab")))
print(list(combinations("ABC", 2)))
print(list(product([0, 1], repeat=2)))Ausgabe:
[1, 2, 'a', 'b']
[('A', 'B'), ('A', 'C'), ('B', 'C')]
[(0, 0), (0, 1), (1, 0), (1, 1)]Merke
- Ein Iterator liefert mit
next()Element für Element;StopIterationbedeutet “fertig” yieldmacht aus einer Funktion einen Generator, der zwischen den Werten pausiert- Generatoren sparen Speicher und können unendlich sein
- Generator-Ausdruck:
(ausdruck for x in folge) - Ein Generator lässt sich nur einmal durchlaufen
itertoolsbringt viele fertige Bausteine
Übungsaufgabe
Schreibe einen Generator gerade_zahlen(grenze), der alle geraden Zahlen bis zur Grenze liefert, und gib seine Werte mit einer Schleife aus.
Quiz zur Selbstkontrolle
Was bewirkt yield in einer Funktion?
- Sie liefert einen Wert und pausiert bis zum nächsten Abruf (richtig)
- Sie beendet die Funktion endgültig
- Sie gibt eine Liste zurück
- Sie startet einen neuen Thread
Wie oft kannst du einen Generator vollständig durchlaufen?
- Nur einmal (richtig)
- Beliebig oft
- Genau zweimal
- So oft, wie er Elemente hat
Wann löst next() StopIteration aus?
- Wenn keine Elemente mehr übrig sind (richtig)
- Wenn das Element None ist
- Wenn der Iterator leer erzeugt wird, nur dann
- Wenn ein Fehler im Element steckt
Weiter im Kurs
Zurück: Rekursion
Weiter: Dekoratoren
Alle Kapitel: Python im Überblick