Python enumerate, zip, map und filter
Lerne, wie Pythons enumerate, zip, map und filter funktionieren – mit klaren Beispielen und praktischen Mustern für saubereren Iterationscode.
Python liefert vier eingebaute Funktionen — enumerate, zip, map und filter — die häufige Schleifenmuster durch einen einzigen, lesbaren Ausdruck ersetzen. Jede gibt einen Iterator zurück (lazy, speichereffizient), und sie lassen sich natürlich mit for-Schleifen und List Comprehensions kombinieren.
Dieses Kapitel behandelt:
enumerate()— Schleifen mit automatischem Indexzip()— zwei oder mehr Sequenzen parallel durchlaufenmap()— eine Funktion auf jedes Element anwendenfilter()— nur Elemente behalten, die einen Test bestehen- Alle vier in realen Mustern kombinieren
- Wann eine List Comprehension vorzuziehen ist
enumerate() — Index beim Schleifen
Wenn du während einer Schleife sowohl die Position als auch den Wert benötigst, verwendet der naive Ansatz einen manuellen Zähler:
colors = ["red", "green", "blue"]
i = 0
for color in colors:
print(i, color)
i += 1enumerate() erledigt das automatisch. Es umhüllt ein beliebiges Iterable und liefert (index, value)-Paare:
colors = ["red", "green", "blue"]
for i, color in enumerate(colors):
print(i, color)Ausgabe:
0 red
1 green
2 blueVon einem anderen Index starten
Übergebe ein start-Argument, um die Zählung bei einer beliebigen ganzen Zahl zu beginnen:
fruits = ["apple", "banana", "cherry"]
for i, fruit in enumerate(fruits, start=1):
print(f"{i}. {fruit}")Ausgabe:
1. apple
2. banana
3. cherryDies ist nützlich beim Erstellen nummerierter Listen für die Anzeige, bei denen 0-basierte Nummerierung für den Leser ungewohnt wirkt.
enumerate() mit einer Bedingung
Da enumerate() ein ganz normaler Iterator ist, können Bedingungen im Schleifenrumpf mit if eingebaut werden:
scores = [72, 91, 55, 88, 44]
for rank, score in enumerate(scores, start=1):
if score >= 80:
print(f"Rank {rank}: score {score} — PASS")Ausgabe:
Rank 2: score 91 — PASS
Rank 4: score 88 — PASSNur den Index ohne enumerate() erhalten
Wenn nur ein Index und sonst nichts benötigt wird, funktioniert range(len(seq)) — sobald jedoch auch der Wert gebraucht wird, sollte enumerate() verwendet werden, um das wiederholte Schreiben von seq[i] zu vermeiden.
zip() — mehrere Sequenzen parallel durchlaufen
zip() kombiniert zwei oder mehr Iterables zu einem einzigen Iterator aus Tupeln und paart Elemente an übereinstimmenden Positionen:
names = ["Alice", "Bob", "Charlie"]
scores = [95, 87, 92]
for name, score in zip(names, scores):
print(f"{name}: {score}")Ausgabe:
Alice: 95
Bob: 87
Charlie: 92zip() mit drei oder mehr Iterables
first = ["a", "b", "c"]
second = [1, 2, 3]
third = [True, False, True]
for x, y, z in zip(first, second, third):
print(x, y, z)Ausgabe:
a 1 True
b 2 False
c 3 TrueSequenzen unterschiedlicher Länge
zip() stoppt beim kürzesten Iterable und verwirft stillschweigend die verbleibenden Elemente längerer Sequenzen:
short = [1, 2]
long = [10, 20, 30, 40]
print(list(zip(short, long)))Ausgabe:
[(1, 10), (2, 20)]Die Werte 30 und 40 aus long werden verworfen. Wenn alle Elemente erhalten bleiben sollen, kann itertools.zip_longest aus der Standardbibliothek verwendet werden:
from itertools import zip_longest
short = [1, 2]
long = [10, 20, 30, 40]
print(list(zip_longest(short, long, fillvalue=0)))Ausgabe:
[(1, 10), (2, 20), (0, 30), (0, 40)]Ein zip-Ergebnis in eine Liste oder ein Dict umwandeln
zip() gibt einen lazy Iterator zurück. Umhülle ihn mit list(), um alle Paare auf einmal zu materialisieren, oder übergib ihn an dict(), um eine Zuordnung aus zwei parallelen Sequenzen zu erstellen:
keys = ["name", "age", "city"]
values = ["Alice", 30, "Paris"]
record = dict(zip(keys, values))
print(record)Ausgabe:
{'name': 'Alice', 'age': 30, 'city': 'Paris'}Entzippen mit zip(*...)
Der *-Entpackungstrick „kehrt" eine Sequenz von Tupeln zurück in separate Sequenzen um:
pairs = [(1, "a"), (2, "b"), (3, "c")]
numbers, letters = zip(*pairs)
print(numbers) # (1, 2, 3)
print(letters) # ('a', 'b', 'c')map() — eine Funktion auf jedes Element anwenden
map(function, iterable) wendet function auf jedes Element an und gibt einen lazy Iterator der Ergebnisse zurück. Es ersetzt eine for-Schleife, die eine neue Liste durch Transformation jedes Elements aufbaut.
numbers = [1, 2, 3, 4, 5]
squares = map(lambda x: x ** 2, numbers)
print(list(squares))Ausgabe:
[1, 4, 9, 16, 25]map() mit einer benannten Funktion
Es kann jedes Callable übergeben werden — eine eingebaute Funktion, eine mit def definierte Funktion oder eine lambda:
words = [" hello ", " world ", " python "]
stripped = list(map(str.strip, words))
print(stripped)Ausgabe:
['hello', 'world', 'python']Hier wird str.strip als Referenz übergeben (ohne Klammern). map() ruft str.strip(word) für jedes Element auf.
map() über mehrere Iterables
Übergebe weitere Iterables, um eine Funktion zu mappen, die zwei oder mehr Argumente entgegennimmt. map() durchläuft dann alle Iterables parallel (wie zip() stoppt es beim kürzesten):
a = [1, 2, 3]
b = [10, 20, 30]
totals = list(map(lambda x, y: x + y, a, b))
print(totals)Ausgabe:
[11, 22, 33]map() vs. List Comprehension
Beide Ansätze sind gültig. Die Faustregel:
| Situation | Bevorzuge |
|---|---|
| Anwenden einer vorhandenen benannten Funktion | map() — kürzer, kein lambda nötig |
| Schreiben eines Inline-Ausdrucks | List Comprehension — besser lesbar |
| Mehrere Eingaben parallel | map() mit mehreren Iterables |
Äquivalente Formen:
# map() with a named function
result = list(map(str.upper, ["a", "b", "c"]))
# list comprehension — same result
result = [s.upper() for s in ["a", "b", "c"]]filter() — nur Elemente behalten, die eine Bedingung erfüllen
filter(function, iterable) behält Elemente, für die function einen wahren Wert zurückgibt. Es gibt einen lazy Iterator zurück.
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = filter(lambda x: x % 2 == 0, numbers)
print(list(evens))Ausgabe:
[2, 4, 6, 8, 10]filter() mit einer benannten Funktion
def is_positive(n):
return n > 0
values = [-3, -1, 0, 4, 7, -2]
positives = list(filter(is_positive, values))
print(positives)Ausgabe:
[4, 7]filter(None, iterable) — falsy-Werte entfernen
Das Übergeben von None als Funktion behält nur Werte, die truthy sind. Dies ist eine schnelle Möglichkeit, leere Strings, Nullen und None-Werte zu entfernen:
mixed = [0, "hello", "", None, 42, False, "world"]
truthy = list(filter(None, mixed))
print(truthy)Ausgabe:
['hello', 42, 'world']filter() vs. List Comprehension
filter() und eine bedingte List Comprehension erledigen dieselbe Aufgabe:
numbers = range(1, 11)
# filter()
evens_f = list(filter(lambda x: x % 2 == 0, numbers))
# list comprehension — equivalent
evens_c = [x for x in numbers if x % 2 == 0]
print(evens_f) # [2, 4, 6, 8, 10]
print(evens_c) # [2, 4, 6, 8, 10]Bevorzuge eine List Comprehension, wenn die Bedingung inline geschrieben wird; bevorzuge filter(), wenn bereits eine gut benannte Prädikatsfunktion vorhanden ist.
enumerate, zip, map und filter kombinieren
Diese vier Funktionen lassen sich natürlich kombinieren. Hier ein realistisches Beispiel: Du hast zwei parallele Listen mit Schülernamen und Rohpunktzahlen; du möchtest einen nummerierten Bericht ausgeben, der nur Schüler enthält, die bestanden haben (Punktzahl >= 60), mit in Buchstabennoten umgerechneten Punktzahlen.
def letter_grade(score):
if score >= 90:
return "A"
elif score >= 80:
return "B"
elif score >= 70:
return "C"
elif score >= 60:
return "D"
return "F"
names = ["Alice", "Bob", "Carol", "Dave", "Eve"]
scores = [85, 52, 91, 67, 48]
# Step 1: pair names with scores
pairs = zip(names, scores)
# Step 2: keep only passing students
passing = filter(lambda pair: pair[1] >= 60, pairs)
# Step 3: convert scores to letter grades
graded = map(lambda pair: (pair[0], letter_grade(pair[1])), passing)
# Step 4: number the results
for rank, (name, grade) in enumerate(graded, start=1):
print(f"{rank}. {name}: {grade}")Ausgabe:
1. Alice: B
2. Carol: A
3. Dave: DJeder Schritt produziert einen Iterator; nichts wird in eine Liste materialisiert, bis die for-Schleife ihn verbraucht. Dies hält den Speicherverbrauch unabhängig von der Eingabegröße konstant.
Häufige Fallstricke
Iteratoren sind nach einem Durchlauf erschöpft
map(), filter() und zip() geben alle Iteratoren zurück, keine Listen. Sobald ein Iterator verbraucht ist, ist er leer:
squares = map(lambda x: x ** 2, [1, 2, 3])
print(list(squares)) # [1, 4, 9]
print(list(squares)) # [] — already exhaustedUmhülle ihn vorab mit list(), wenn du mehr als einmal iterieren musst.
zip() verwirft stillschweigend überschüssige Elemente
Wenn die Eingabesequenzen unterschiedliche Längen haben, verwirft zip() die zusätzlichen Elemente ohne Warnung. Überprüfe Längen explizit, wenn dies wichtig ist, oder verwende itertools.zip_longest.
enumerate() ändert das Iterable nicht
enumerate() umhüllt das ursprüngliche Iterable; es kopiert es nicht. Das Ändern der zugrunde liegenden Liste während der Iteration kann zu unerwarteten Ergebnissen führen — dieselbe Vorsicht, die für gewöhnliche for-Schleifen gilt.
Kurzreferenz
| Funktion | Signatur | Was sie zurückgibt |
|---|---|---|
enumerate | enumerate(iterable, start=0) | Iterator von (index, value)-Tupeln |
zip | zip(*iterables) | Iterator von Tupeln, ein Element aus jedem Iterable pro Tupel |
map | map(function, *iterables) | Iterator von function, angewendet auf jedes Element |
filter | filter(function, iterable) | Iterator von Elementen, bei denen function True zurückgibt |
Alle vier sind lazy: Sie produzieren Werte bei Bedarf, anstatt vorab eine vollständige Liste aufzubauen.