W3docs

Python-Dictionary- und Set-Comprehensions

Dictionary-Comprehensions, Set-Comprehensions und Generator-Ausdrücke in Python meistern — mit Syntax, Beispielen und typischen Fallstricken.

Dictionary-Comprehensions, Set-Comprehensions und Generator-Ausdrücke erweitern die kompakte Syntax der List Comprehension auf andere Datenstrukturen. Dieses Kapitel erklärt jede Form ausführlich — Syntax, Filterung, Verschachtelung, praxisnahe Muster und wann man sie vermeiden sollte.

Dictionary Comprehensions

Eine Dictionary Comprehension erstellt ein neues dict aus einem beliebigen Iterable in einem einzigen Ausdruck. Anstatt eine for-Schleife zu schreiben, die bei jeder Iteration d[key] = value aufruft, beschreibt man die Zuordnung prägnant innerhalb geschweifter Klammern.

Syntax

new_dict = {key_expr: value_expr for item in iterable}

Eine optionale if-Bedingung kann nach dem Iterable angefügt werden:

new_dict = {key_expr: value_expr for item in iterable if condition}
TeilRolle
key_exprAusdruck, der jeden Schlüssel erzeugt
value_exprAusdruck, der jeden Wert erzeugt
itemSchleifenvariable — nimmt nacheinander jeden Wert aus iterable an
if conditionOptionaler Filter — überspringt Elemente, bei denen condition False ist

Einfaches Beispiel: Eine Quadrat-Map erstellen

squares = {x: x ** 2 for x in range(1, 6)}
print(squares)
# {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

Die entsprechende for-Schleife:

squares = {}
for x in range(1, 6):
    squares[x] = x ** 2

Beide liefern dasselbe Ergebnis; die Comprehension-Form ist prägnanter und bringt die Absicht auf einen Blick zum Ausdruck.

Einträge filtern

Nur die Paare behalten, die eine Bedingung erfüllen:

prices = {"apple": 1.20, "banana": 0.50, "orange": 0.80, "grape": 2.50}

expensive = {item: price for item, price in prices.items() if price >= 1.00}
print(expensive)
# {'apple': 1.2, 'grape': 2.5}

Werte transformieren

Eine Funktion oder einen arithmetischen Ausdruck auf jeden Wert anwenden:

prices = {"apple": 1.20, "banana": 0.50, "orange": 0.80}

# Apply a 10% discount and round to 2 decimal places
discounted = {item: round(price * 0.9, 2) for item, price in prices.items()}
print(discounted)
# {'apple': 1.08, 'banana': 0.45, 'orange': 0.72}

Schlüssel und Werte tauschen

Ein Dictionary umkehren (funktioniert korrekt, wenn alle Werte eindeutig sind):

capitals = {"France": "Paris", "Germany": "Berlin", "Japan": "Tokyo"}

inverted = {city: country for country, city in capitals.items()}
print(inverted)
# {'Paris': 'France', 'Berlin': 'Germany', 'Tokyo': 'Japan'}

Wenn Werte nicht eindeutig sind, überschreiben spätere Einträge frühere. Dies sollte nur verwendet werden, wenn man sicher ist, dass die Werte eine Eins-zu-eins-Zuordnung bilden.

Aus zwei Iterables mit zip() aufbauen

zip() koppelt Elemente aus zwei Sequenzen und macht es einfach, ein Dictionary aus separaten Schlüssel- und Wertelisten zu erstellen:

keys = ["name", "age", "city"]
values = ["Alice", 30, "Berlin"]

profile = {k: v for k, v in zip(keys, values)}
print(profile)
# {'name': 'Alice', 'age': 30, 'city': 'Berlin'}

Dies entspricht dict(zip(keys, values)), aber die Comprehension-Form macht es einfach, gleichzeitig einen Filter oder eine Werttransformation hinzuzufügen.

Schlüssel normalisieren

Eine häufige Aufgabe in der Praxis ist das Bereinigen von Dictionary-Schlüsseln — zum Beispiel alle Schlüssel in Kleinbuchstaben umwandeln, wenn Daten aus verschiedenen Quellen zusammengeführt werden:

raw = {"Name": "Alice", "AGE": 30, "City": "Berlin"}

normalised = {k.lower(): v for k, v in raw.items()}
print(normalised)
# {'name': 'Alice', 'age': 30, 'city': 'Berlin'}

Bedingter Wertausdruck (Ternary im Wert)

Im Wertbereich kann ein ternärer Ausdruck verwendet werden, um pro Element zwischen zwei Werten zu wählen:

scores = {"Alice": 95, "Bob": 62, "Carol": 78, "Dave": 45}

grades = {name: "pass" if score >= 70 else "fail" for name, score in scores.items()}
print(grades)
# {'Alice': 'pass', 'Bob': 'fail', 'Carol': 'pass', 'Dave': 'fail'}

Verschachtelte Dictionary Comprehensions

Comprehensions können verschachtelt werden, um mehrstufige Daten zu verarbeiten. Zum Beispiel das Extrahieren eines Feldes aus einer Liste verschachtelter Dictionaries:

students = [
    {"name": "Alice", "score": 95, "grade": "A"},
    {"name": "Bob",   "score": 82, "grade": "B"},
    {"name": "Carol", "score": 91, "grade": "A"},
]

# Build a {name: score} mapping from the list
name_to_score = {s["name"]: s["score"] for s in students}
print(name_to_score)
# {'Alice': 95, 'Bob': 82, 'Carol': 91}

Die Verschachtelung sollte flach bleiben. Wenn die Logik schwer nachvollziehbar wird, eine Hilfsfunktion extrahieren oder eine gewöhnliche for-Schleife verwenden.

Set Comprehensions

Eine Set Comprehension erstellt ein set in einem einzigen Ausdruck. Da Sets nur eindeutige Elemente enthalten, werden Duplikate automatisch entfernt.

Syntax

new_set = {expression for item in iterable}
new_set = {expression for item in iterable if condition}

Der einzige visuelle Unterschied zu einer Dict Comprehension ist das Fehlen des Doppelpunkts — es gibt einen Ausdruck, kein key: value-Paar.

Einfaches Beispiel: Eindeutige Quadrate

numbers = [1, 2, 2, 3, 3, 3, 4]
unique_squares = {x ** 2 for x in numbers}
print(unique_squares)
# {1, 4, 9, 16}

Die Ausgabereihenfolge ist nicht garantiert — Sets sind ungeordnet, daher sollte man sich nicht auf eine bestimmte Anzeigereihenfolge verlassen.

Deduplizieren beim Transformieren

Ein verbreitetes Muster ist das Normalisieren von Strings und das Deduplizieren in einem Schritt:

tags = ["Python", "python", "PYTHON", "Data", "data", "DATA"]

unique_tags = {tag.lower() for tag in tags}
print(unique_tags)
# {'python', 'data'}

Filtern mit einer Bedingung

words = ["cat", "elephant", "dog", "rhinoceros", "ant"]

long_words = {w for w in words if len(w) > 4}
print(long_words)
# {'elephant', 'rhinoceros'}

Gemeinsame Elemente finden

Set Comprehensions lassen sich gut mit Mengenoperationen kombinieren:

list_a = [1, 2, 3, 4, 5, 5, 6]
list_b = [4, 5, 6, 7, 8]

set_a = {x for x in list_a}
set_b = {x for x in list_b}

common = set_a & set_b
print(common)
# {4, 5, 6}

Für eine einfache Konvertierung ohne Transformation ist set(list_a) einfacher. Eine Set Comprehension sollte verwendet werden, wenn beim Konvertieren gefiltert oder transformiert werden soll.

Generator-Ausdrücke

Ein Generator-Ausdruck sieht aus wie eine List Comprehension, verwendet aber runde Klammern statt eckiger. Anstatt die gesamte Sammlung auf einmal im Speicher aufzubauen, erzeugt er einen Wert nach dem anderen, bei Bedarf (lazy).

Syntax

gen = (expression for item in iterable)
gen = (expression for item in iterable if condition)

Warum einen Generator-Ausdruck verwenden?

SzenarioList ComprehensionGenerator-Ausdruck
Ergebnis muss mehrfach iteriert werdenJaNein — Generatoren sind nach einem Durchlauf erschöpft
Wahlfreier Zugriff per Index (result[3]) nötigJaNein
Ergebnis wird an sum(), max(), any() etc. übergebenFunktioniert, aber alloziert eine ListeBevorzugt — streamt Werte ohne Liste aufzubauen
Sehr große oder unendliche SequenzKann den Speicher erschöpfenEffizient — ein Wert nach dem anderen

Einfaches Beispiel

# List comprehension — builds the entire list in memory
squares_list = [x ** 2 for x in range(1, 6)]
print(squares_list)   # [1, 4, 9, 16, 25]

# Generator expression — yields values one at a time
squares_gen = (x ** 2 for x in range(1, 6))
print(squares_gen)    # <generator object <genexpr> at 0x...>
print(list(squares_gen))  # [1, 4, 9, 16, 25]

Das Generator-Objekt selbst ist nicht die Liste — man verbraucht es durch Iteration oder Übergabe an eine Funktion.

Direkt an eingebaute Funktionen übergeben

Wenn ein Generator-Ausdruck als einziges Argument an eine Funktion übergeben wird, können die zusätzlichen runden Klammern weggelassen werden:

total = sum(x ** 2 for x in range(1, 1001))
print(total)  # 333833500

maximum = max(len(word) for word in ["apple", "banana", "kiwi"])
print(maximum)  # 6

any_negative = any(x < 0 for x in [1, -2, 3])
print(any_negative)  # True

Speichervorteil

Bei großen Datenmengen vermeidet ein Generator-Ausdruck das Laden aller Daten in den RAM:

# Simulate a large log file as a list of strings
log_lines = [f"ERROR line {i}" if i % 100 == 0 else f"INFO line {i}" for i in range(1_000_000)]

# Generator scans lines without building an intermediate list
error_count = sum(1 for line in log_lines if line.startswith("ERROR"))
print(error_count)  # 10000

Generatoren sind nach einem Durchlauf erschöpft

Dies ist der häufigste Fallstrick:

gen = (x * 2 for x in range(5))

print(list(gen))  # [0, 2, 4, 6, 8]
print(list(gen))  # [] — the generator is now empty

Wenn das Ergebnis mehr als einmal iteriert werden muss, entweder eine List Comprehension verwenden oder den Generator neu erstellen.

Generator-Ausdrücke verketten

Generator-Ausdrücke können ohne Zwischenlisten kombiniert werden. Jede Stufe zieht Werte aus der vorherigen:

numbers = range(1, 11)

# Stage 1: filter even numbers
evens = (x for x in numbers if x % 2 == 0)

# Stage 2: square them
even_squares = (x ** 2 for x in evens)

print(list(even_squares))  # [4, 16, 36, 64, 100]

Es wird keine Zwischenliste erstellt — Werte fließen einzeln durch die Pipeline.

Die richtige Form wählen

Sie benötigen…Verwenden Sie
Eine Liste transformierter/gefilterter Werte[expr for item in it]
Ein Dictionary aus Wertepaaren{k: v for item in it}
Eine Sammlung ohne Duplikate{expr for item in it}
Speichereffiziente Einmal-Iteration(expr for item in it)
Komplexe, mehrzeilige Logik pro ElementGewöhnliche for-Schleife

Häufige Fallstricke

Dict Comprehension vs. Set Comprehension. Beide verwenden geschweifte Klammern {}. Der Unterschied liegt darin, ob man key: value (dict) oder einen einzelnen expression (set) schreibt. Ein leeres {} erstellt immer ein leeres dict, kein leeres Set — für ein leeres Set set() verwenden.

d = {}     # empty dict
s = set()  # empty set — NOT {}

Überschriebene Schlüssel. Wenn der Schlüsselausdruck Duplikate erzeugt, überschreiben spätere Werte stillschweigend frühere:

data = [("a", 1), ("b", 2), ("a", 99)]
d = {k: v for k, v in data}
print(d)  # {'a': 99, 'b': 2} — first 'a' is gone

Variablenbereich. In Python 3 ist die Schleifenvariable in einer Comprehension lokal zur Comprehension und dringt nicht in den umgebenden Bereich vor:

x = "original"
result = {x: x.upper() for x in ["a", "b", "c"]}
print(x)  # 'original' — comprehension's x did not overwrite this

Lesbarkeitsgrenze. Wenn mehr als eine if-Bedingung benötigt wird oder der Ausdruck lang ist, ist eine gewöhnliche for-Schleife mit beschreibenden Variablennamen in der Regel übersichtlicher:

# Hard to read
result = {k: v for k, v in data.items() if k.startswith("user_") if v is not None}

# Easier to read
result = {}
for k, v in data.items():
    if k.startswith("user_") and v is not None:
        result[k] = v

Verwandte Themen

  • List Comprehension — die Grundlage: Syntax, Filterung, verschachtelte Schleifen und wann eine gewöhnliche for-Schleife besser ist
  • Python Dictionaries — Dictionary-Grundlagen, Erstellung und Zugriff
  • Python Sets — Set-Erstellung, Operationen und Anwendungsfälle
  • Loop Dictionaries — alle Techniken zum Iterieren über Dictionaries
  • Python Iterators — wie das Iterator-Protokoll von Python unter der Haube funktioniert

Übungen

Übung
Which of the following statements about Python comprehensions is correct?
Which of the following statements about Python comprehensions is correct?
Was this page helpful?