Lehrpfad 4 • Professional Data Engineering

Generatoren, Iteratoren & itertools 🌊⚡

Verarbeite riesige Datenströme mit 0 RAM-Verschwendung: Entdecke das Geheimnis von yield, Lazy Evaluation und Pythons High-Performance-Modul itertools.

📂 Dateien dieser Einheit:
📄 aufgabe.py (Startercode) 🧪 test_aufgabe.py (Tests) 💡 musterloesung.py (Lösung) 📖 README.md

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)]
⚠️ Gefahr: Out of Memory Crash bei Big Data!

🟢 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))
✨ 100.000x speichereffizienter & ultraschnell!
💡 Kernprinzip: Lazy Evaluation (Verzögerte Auswertung)

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.
Iteratoren manuell steuern mit iter() und next()
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!

Endloszähler & Fibonacci mit yield
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:

Terminal (im Ordner 19_generatoren_und_itertools)
python3 test_aufgabe.py

2. Benchmark-Skript starten:

Terminal
python3 aufgabe.py