Sobald List Comprehensions klick machen, wird man nie wieder eine for-Schleife schreiben, die an eine Liste anhängt. Sie sind nicht nur syntaktischer Zucker — sie signalisieren die Absicht klar und laufen dank Bytecode-Optimierung schneller als die äquivalente Schleife in CPython. Laut dem offiziellen Python-Tutorial bieten Comprehensions eine prägnante Möglichkeit, Listen auf Basis bestehender Sequenzen oder anderer Iterables zu erstellen. Dieser Artikel baut zuerst das mentale Modell auf und deckt dann jedes echte Muster ab: Filtern, Verschachteln, Dict- und Set-Comprehensions, und den einen Fall, in dem man lieber eine einfache for-Schleife verwenden sollte.

Das grundlegende Muster

Die Anatomie einer List Comprehension ist [Ausdruck for Element in Iterable]. Drei Teile: der Ausgabe-Ausdruck (zu was jedes Element wird), die Schleifenvariable und das Iterable, aus dem gezogen wird. Mit einer vertrauten Schleife beginnen und sie zusammenfalten.

python
# Plain for loop — building a list of file sizes in KB
file_sizes_bytes = [1024, 204800, 51200, 3145728, 8192]

sizes_kb = []
for size in file_sizes_bytes:
    sizes_kb.append(size / 1024)

# print(sizes_kb)  →  [1.0, 200.0, 50.0, 3072.0, 8.0]

# Same result as a list comprehension — one line, same meaning
sizes_kb = [size / 1024 for size in file_sizes_bytes]

Die Comprehension liest sich fast wie Englisch: „Gib mir size / 1024 für jedes size in file_sizes_bytes." Diese Klarheit ist der eigentliche Gewinn — ein Leser muss keinen append-Aufruf verfolgen, um zu verstehen, was gebaut wird.

python
# Another common pattern: deriving one list from another
usernames = ["alice", "bob", "carol"]

# Build a list of display names
display_names = [name.capitalize() for name in usernames]
# ['Alice', 'Bob', 'Carol']

# Or extract a single field from a list of dicts
users = [
    {"id": 1, "name": "Alice", "role": "admin"},
    {"id": 2, "name": "Bob",   "role": "viewer"},
    {"id": 3, "name": "Carol", "role": "editor"},
]

names = [user["name"] for user in users]
# ['Alice', 'Bob', 'Carol']

Filtern mit if

Eine Bedingung am Ende hinzufügen, und nur Elemente, die sie bestehen, kommen in die Ausgabeliste: [Ausdruck for Element in Iterable if Bedingung]. Dies ist das Muster, das eine for- + if- + append-Kombination ersetzt.

python
# Loop version — extract only active users
active_users = []
for user in users:
    if user["active"]:
        active_users.append(user["name"])

# Comprehension version — identical result
users = [
    {"name": "Alice", "active": True},
    {"name": "Bob",   "active": False},
    {"name": "Carol", "active": True},
    {"name": "Dave",  "active": False},
]

active_users = [user["name"] for user in users if user["active"]]
# ['Alice', 'Carol']
python
# Filtering a raw CSV row — drop blanks and whitespace-only values
raw_row = ["[email protected]", "", "  ", "editor", " "]

clean_row = [field.strip() for field in raw_row if field.strip()]
# ['[email protected]', 'editor']

# Filtering a list of log levels
log_lines = [
    "INFO  server started",
    "DEBUG loading config",
    "ERROR database timeout",
    "DEBUG query took 450ms",
    "ERROR disk space low",
]

errors = [line for line in log_lines if line.startswith("ERROR")]
# ['ERROR database timeout', 'ERROR disk space low']

Arbeiten mit Strings

String-Verarbeitung ist der Bereich, wo Comprehensions ihren Wert beweisen. Die Kombination aus Pythons reichhaltigen String-Methoden und der Comprehension-Syntax hält Transformationspipelines lesbar, ohne Zwischenvariablen.

python
# Strip whitespace from tags coming out of a form field
raw_tags = ["  python ", "data science", " machine-learning ", "API"]

tags = [tag.strip().lower() for tag in raw_tags]
# ['python', 'data science', 'machine-learning', 'api']

# Normalise email addresses from a signup CSV
raw_emails = ["[email protected]", "  [email protected]  ", "[email protected]"]

emails = [e.strip().lower() for e in raw_emails]
# ['[email protected]', '[email protected]', '[email protected]']

# Extract file extensions from a list of uploaded filenames
filenames = ["report.pdf", "avatar.PNG", "data.CSV", "archive.tar.gz", "notes.txt"]

extensions = [name.rsplit(".", 1)[-1].lower() for name in filenames if "." in name]
# ['pdf', 'png', 'csv', 'gz', 'txt']
Tipp: Beim Normalisieren von Benutzereingaben sollte man vor dem Kleinschreiben stripppen — sonst besteht ein Wert wie " [email protected] " eine .lower()-Prüfung auf dem Inhalt, führt aber immer noch führende Leerzeichen mit sich, die eine Datenbanksuche oder JSON-Schlüsselabgleich brechen.

Dict- und Set-Comprehensions

Dieselbe Idee erstreckt sich auf Dicts und Sets. Eine Dict Comprehension verwendet geschweifte Klammern mit einem Schlüssel: Wert-Paar: {Schlüssel: Wert for Element in Iterable}. Eine Set Comprehension lässt den Doppelpunkt weg und erzeugt eine deduplizierte Sammlung: {Ausdruck for Element in Iterable}.

python
# Invert a dict — swap keys and values
permissions = {"alice": "admin", "bob": "viewer", "carol": "editor"}

by_role = {role: name for name, role in permissions.items()}
# {'admin': 'alice', 'viewer': 'bob', 'editor': 'carol'}

# Build a fast lookup dict from a list of user records
users = [
    {"id": 101, "name": "Alice", "active": True},
    {"id": 102, "name": "Bob",   "active": False},
    {"id": 103, "name": "Carol", "active": True},
]

# O(1) lookups by ID — much faster than scanning the list every time
user_by_id = {user["id"]: user for user in users}
# {101: {...}, 102: {...}, 103: {...}}

# Access a user directly
user_by_id[102]["name"]  # 'Bob'
python
# Set comprehension — deduplicate a list of file extensions
uploads = ["report.pdf", "data.csv", "summary.pdf", "export.CSV", "notes.txt"]

unique_extensions = {name.rsplit(".", 1)[-1].lower() for name in uploads if "." in name}
# {'pdf', 'csv', 'txt'}  — order not guaranteed

Ein Vorbehalt mit Sets: Die Reihenfolge wird nicht bewahrt. Wenn man eine Liste deduplizieren und dabei die ursprüngliche Reihenfolge beibehalten möchte, ist eine Set Comprehension das falsche Werkzeug — stattdessen list(dict.fromkeys(items)) verwenden, was das Einfüge-geordnete Verhalten von Dicts in Python 3.7+ nutzt.

Verschachtelte Comprehensions

Man kann Comprehensions verschachteln, um über verschachtelte Strukturen zu iterieren. Der häufigste Anwendungsfall ist das Glätten einer Liste von Listen — eine Matrix aus einem CSV-Parse, paginierte API-Antwortseiten oder gruppierte Abfrageergebnisse.

python
# Flatten a 2D list (e.g. paginated API results)
pages = [
    [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}],
    [{"id": 3, "name": "Carol"}],
    [{"id": 4, "name": "Dave"}, {"id": 5, "name": "Eve"}],
]

all_users = [user for page in pages for user in page]
# [{'id': 1, ...}, {'id': 2, ...}, {'id': 3, ...}, {'id': 4, ...}, {'id': 5, ...}]

# The order mirrors what nested for loops would produce:
# for page in pages:
#     for user in page:
#         all_users.append(user)

Verschachtelte Comprehensions von links nach rechts lesen — die äußere Schleife kommt zuerst, die innere Schleife als zweites. Das entspricht der Reihenfolge verschachtelter for-Schleifen.

python
# Two levels — fine and readable
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [cell for row in matrix for cell in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

# Three levels — stop here. Use a plain loop or a helper function.
# This is the point where readability dies:
cube = [[[1,2],[3,4]],[[5,6],[7,8]]]

# Don't do this:
flat3 = [val for layer in cube for row in layer for val in row]

# Do this instead:
flat3 = []
for layer in cube:
    for row in layer:
        flat3.extend(row)
Generator-Ausdrücke verwenden dieselbe Syntax mit runden Klammern statt eckigen: (user["id"] for user in users). Sie werden lazy ausgewertet — Elemente werden einzeln erzeugt, anstatt die gesamte Liste im Speicher aufzubauen. Sie verwenden, wenn man nur einmal iterieren muss oder das Ergebnis direkt an sum(), max(), any() oder ähnliche Funktionen übergibt. Siehe die Generator-Ausdrücke-Referenz für die vollständigen Details.

Der if/else-Ternary innerhalb einer Comprehension

Wenn man Elemente in einen von zwei Werten transformieren muss — anstatt sie zu verwerfen — verwendet man einen Ternary-Ausdruck in der Ausgabeposition. Die Position ist wichtig: der Ternary gehört an den Anfang, nicht nach das Iterable.

python
# Correct: ternary is the output expression
users = [
    {"name": "Alice", "active": True},
    {"name": "Bob",   "active": False},
    {"name": "Carol", "active": True},
]

status = ["active" if user["active"] else "inactive" for user in users]
# ['active', 'inactive', 'active']

# Normalise a config value — replace None with a default
raw_config = ["/var/log", None, "/tmp", None, "/etc/app"]

paths = [path if path is not None else "/var/log/default" for path in raw_config]
# ['/var/log', '/var/log/default', '/tmp', '/var/log/default', '/etc/app']
python
# Common mistake — putting the ternary after the iterable (SyntaxError)
# status = ["active" for user in users if user["active"] else "inactive"]  # WRONG

# The trailing "if" is a filter — it drops non-matching items entirely.
# The ternary ("if ... else ...") in the output expression transforms all items.
# They serve different purposes. You can combine them:

# Keep only users, but show their status
status = ["active" if user["active"] else "inactive"
          for user in users
          if user["name"] != "Bob"]
# ['active', 'active']  — Bob was filtered out entirely

Wann stattdessen eine for-Schleife verwenden

List Comprehensions sind zum Aufbauen von Listen. Wenn man wirklich Nebeneffekte ausführt, eine for-Schleife verwenden — keine Comprehension. Dies geht über Stil hinaus: Eine Comprehension, die ihr Ergebnis verwirft, verschwendet Speicher beim Aufbauen einer Liste, die niemand verwendet, und verbirgt die Absicht vor dem Leser.

python
# Bad — comprehension for side effects (writing to a file, printing, calling an API)
[print(f"Processing user {user['name']}") for user in users]   # don't do this
[requests.post("/api/notify", json=user) for user in users]    # definitely don't do this

# Good — plain for loop makes the intent obvious
for user in users:
    print(f"Processing user {user['name']}")

for user in users:
    requests.post("/api/notify", json=user)
python
# Bad — comprehension with logic complex enough to need comments or multiple steps
result = [
    user["name"].strip().lower()
    if user.get("active") and user.get("email_verified")
    else user["name"].strip().lower() + " (unverified)"
    for user in users
    if user.get("role") in ("admin", "editor") and user.get("last_login") is not None
]

# Good — break it out when the logic is this involved
result = []
for user in users:
    if user.get("role") not in ("admin", "editor"):
        continue
    if user.get("last_login") is None:
        continue
    name = user["name"].strip().lower()
    if not (user.get("active") and user.get("email_verified")):
        name += " (unverified)"
    result.append(name)
  • Eine Comprehension verwenden, wenn man eine neue Liste aus einem bestehenden Iterable mit einem klaren Ausdruck und einem optionalen Filter aufbaut.
  • Eine for-Schleife verwenden, wenn man Nebeneffekte ausführt — I/O, Netzwerkaufrufe, Drucken, Mutieren von externem Zustand.
  • Eine for-Schleife verwenden, wenn die Transformationslogik mehrere Zeilen, Zwischenvariablen oder Kommentare benötigt, um verstanden zu werden.
  • Einen Generator-Ausdruck verwenden, wenn man nur einmal iterieren muss oder direkt an sum(), any(), max() übergibt — keine Liste im Speicher nötig.

Leistungshinweis

List Comprehensions sind deutlich schneller als eine äquivalente for- + append-Schleife in CPython. Der Grund ist Bytecode: Eine Comprehension kompiliert zu einem dedizierten LIST_APPEND-Opcode, der das Attribut-Lookup auf list.append bei jeder Iteration vermeidet. Das Python-Performance-Tips-Wiki behandelt dies, und der Unterschied beträgt typischerweise 10–40% für reine Python-Arbeitslasten, abhängig von der Listengröße.

python
import timeit

data = list(range(100_000))

# for + append
def with_loop():
    result = []
    for x in data:
        result.append(x * 2)
    return result

# list comprehension
def with_comprehension():
    return [x * 2 for x in data]

# generator expression — no list built at all
def with_generator():
    return sum(x * 2 for x in data)

# Typical results on CPython 3.12:
# with_loop():          ~7.2 ms
# with_comprehension(): ~4.8 ms  (~33% faster)
# with_generator():     ~4.1 ms  (and uses O(1) memory vs O(n))

Wenn man keine materialisierte Liste benötigt — das Ergebnis wird an sum(), any(), max() übergeben oder einmal iteriert — einen Generator-Ausdruck verwenden. Er verwendet konstanten Speicher unabhängig von der Eingabegröße, was wichtig ist, wenn große CSV-Exporte oder JSON-Nutzlasten in einer engen Schleife verarbeitet werden.

Zusammenfassung

List Comprehensions sind eines dieser Python-Features, die sich etwa eine Woche lang ungewohnt anfühlen und dann unersetzlich werden. Das mentale Modell ist einfach: Ausgabe-Ausdruck, Schleifenvariable, Iterable, optionaler Filter. Daran festhalten und man schreibt lesbares, idiomatisches Python. Wenn die Logik komplex wird, ist das das Signal, auf eine einfache Schleife zurückzufallen — kein Versagen, nur das richtige Werkzeug für die Aufgabe.

Wenn man mit JSON-Daten in Python arbeitet — API-Antworten in Listen von Werten umwandeln, Felder aus Datensätzen extrahieren, Lookup-Dicts aufbauen — passen die Tools auf dieser Seite gut zu dem, was gerade gelernt wurde. Den JSON Formatter ausprobieren, um JSON-Nutzlasten zu inspizieren und hübsch zu drucken, bevor man die Comprehension schreibt, die sie verarbeitet, oder den CSV Formatter, um CSV-Daten zu validieren, bevor man sie in eine Liste von Zeilen parst. Für die vollständige Sprachreferenz sind die Python-Dokumentation zu List Comprehensions und PEP 202 (der ursprüngliche Vorschlag) lesenswert.