1. Warum Generatoren? Die Badewannen-Analogie 🚰🛁
Stelle dir vor, du möchtest 10 Millionen Sensor-Messwerte analysieren. Wie verarbeitet ein Rechner diese Daten?
🔴 Die Badewanne (Klassische Listen)
Du lässt erst die gesamte Badewanne vollaufen (alle 10 Mio. Werte im RAM erzeugen), bevor du einen Schluck nimmst.
# Belegt ~80 MB bis 1 GB RAM! daten = [x**2 for x in range(10_000_000)]
🟢 Der Wasserhahn (Generatoren mit yield)
Du drehst den Hahn auf: Die Daten fließen Tropfen für Tropfen (on-demand) direkt in deinen Mund.
# Belegt nur ~104 Bytes RAM! daten = (x**2 for x in range(10_000_000))
Ein Generator berechnet den nächsten Wert erst in der exakten Millisekunde, in der du ihn mit next() oder in einer for-Schleife abfragst!
2. Das Protokoll: Iterable vs. Iterator 🔄
Jedes Mal, wenn du in Python eine for-Schleife schreibst, passiert Folgendes:
| Konzept | Magische Methode | Bedeutung | Code-Beispiel |
|---|---|---|---|
| Iterable | __iter__() |
Objekt, über das iteriert werden kann. | liste = [1, 2, 3] |
| Iterator | __next__() |
Zeiger-Objekt, das den aktuellen Zustand hält. | it = iter(liste); next(it) |
| StopIteration | Exception | Wird automatisch geworfen, wenn alle Werte verbraucht sind. | Beendet for-Schleifen lautlos. |
farben = ["Rot", "Gruen", "Blau"] it = iter(farben) print(next(it)) # -> "Rot" print(next(it)) # -> "Gruen" print(next(it)) # -> "Blau" # next(it) wirft jetzt StopIteration!
3. Magie im Einsatz: Eigene Generatoren mit yield 🪄
Im Gegensatz zu return (welches eine Funktion komplett beendet) unterbricht yield die Funktion nur kurzzeitig, gibt den Wert zurück und friert den gesamten Zustand (lokale Variablen, Zeilennummer) ein!
def endlos_zaehler(start=0, schritt=1): aktuell = start while True: yield aktuell # Pausiert hier und gibt den Wert zurück aktuell += schritt # Macht beim nächsten next() genau hier weiter! z = endlos_zaehler(10, 5) print(next(z)) # -> 10 print(next(z)) # -> 15 print(next(z)) # -> 20
4. Das itertools Power-Modul 🧰
Das eingebaute Modul itertools bietet blitzschnelle, in C geschriebene Iterator-Bausteine:
| Werkzeug | Zweck | Beispiel |
|---|---|---|
itertools.chain(*iterables) |
Verkettet mehrere Listen/Generatoren ohne Kopieren | chain([1, 2], [3, 4]) → 1, 2, 3, 4 |
itertools.cycle(iterable) |
Wiederholt eine Liste unendlich oft im Kreis | cycle(['Rot', 'Gruen']) → Rot, Gruen, Rot... |
itertools.permutations(p, r) |
Alle geordneten Anordnungen (Passwort-Cracker) | permutations('AB', 2) → ('A','B'), ('B','A') |
itertools.combinations(p, r) |
Alle ungeordneten Teilmengen (Lotto, Teams) | combinations('ABC', 2) → ('A','B'), ('A','C'), ('B','C') |
5. 🎯 Deine Aufgaben in aufgabe.py
Öffne die Datei aufgabe.py und setze die folgenden Meilensteine um:
💡 Lösungshinweise (Aufklappen bei Bedarf)
🔍 Tipp zu TODO 2: Fibonacci mit yield
Validiere zuerst die Eingabe und iteriere mit einer Zählschleife:
if anzahl < 0: raise ValueError("Anzahl darf nicht negativ sein!") a, b = 0, 1 for _ in range(anzahl): yield a a, b = b, a + b
🔍 Tipp zu TODO 6: Passwort-Permutationen als String-Liste
itertools.permutations liefert Tupel von Zeichen. Mit "".join() machst du daraus Strings:
return ["".join(p) for p in itertools.permutations(zeichen, laenge)]
6. 🧪 Lösung überprüfen & Testen
1. Automatische Tests ausführen:
python3 test_aufgabe.py
2. Benchmark-Skript starten:
python3 aufgabe.py