Zodra list comprehensions klikken, schrijf je nooit meer een for-lus die aan een lijst toevoegt.
Ze zijn niet alleen syntactische suiker — ze geven duidelijk intentie aan en lopen sneller dan de equivalente lus in CPython
dankzij bytecode-optimalisatie. Volgens de
officiële Python-tutorial
bieden comprehensions een beknopte manier om lijsten te maken op basis van bestaande reeksen of andere iterables.
Dit artikel bouwt eerst het mentale model op en behandelt daarna elk echt patroon: filteren, nesten,
dict- en setcomprehensions, en het ene geval waarbij je toch naar een gewone for-lus moet grijpen.
Het Basispatroon
De anatomie van een list comprehension is [expressie for item in iterable].
Drie delen: de uitvoerexpressie (wat elk element wordt), de lusvariabele
en de iterable om uit te putten. Begin met een bekende lus en vouw die samen.
# 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]De comprehension leest bijna als Nederlands: "geef me size / 1024 voor elke size
in file_sizes_bytes." Die duidelijkheid is de echte winst — een lezer hoeft geen
append-aanroep te traceren om te begrijpen wat je aan het bouwen bent.
# 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']Filteren met if
Voeg een conditie toe aan het einde en alleen elementen die slagen komen in de uitvoerlijst:
[expressie for item in iterable if conditie].
Dit is het patroon dat een combinatie van for + if + append vervangt.
# 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']# 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']Werken met Strings
Stringverwerking is waar comprehensions hun waarde bewijzen. De combinatie van Python's rijke stringmethoden en comprehension-syntaxis houdt transformatiepipelines leesbaar zonder tussenliggende variabelen.
# 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']" [email protected] " een .lower()-controle op de inhoud door maar draagt nog steeds
voorloopspaties die een databaseopzoeking of JSON-sleutelovereenkomst zullen breken.Dict- en Setcomprehensions
Hetzelfde idee strekt zich uit naar dicts en sets. Een
dict comprehension
gebruikt accolades met een sleutel: waarde-paar: {sleutel: waarde for item in iterable}.
Een setcomprehension laat de dubbele punt weg en produceert een gededupliceerde verzameling:
{expressie for item in iterable}.
# 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'# 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 guaranteedEén voorbehoud bij sets: volgorde wordt niet bewaard. Als je een lijst wilt dedupliceren terwijl
je de oorspronkelijke volgorde behoudt, is een setcomprehension het verkeerde gereedschap — gebruik
list(dict.fromkeys(items)) in plaats daarvan, wat het invoegvolgorde-gedrag van
dicts in Python 3.7+ benut.
Geneste Comprehensions
Je kunt comprehensions nesten om over geneste structuren te itereren. Het meest voorkomende gebruiksscenario is het platmaken van een lijst van lijsten — een matrix van een CSV-parse, gesegmenteerde API-responsepagina's, of gegroepeerde queryresultaten.
# 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)Lees geneste comprehensions van links naar rechts — de buitenste lus komt eerst, de binnenste lus tweede.
Dat komt overeen met de volgorde van geneste for-lussen.
# 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)(user["id"] for user in users). Ze evalueren lazy — elementen worden één voor één geproduceerd
in plaats van de volledige lijst in geheugen te bouwen. Gebruik ze wanneer je maar één keer hoeft te itereren, of wanneer je
het resultaat direct doorgeeft aan sum(), max(), any() of vergelijkbare functies.
Zie de referentie voor generatorexpressies voor alle details.De if/else Ternaire Expressie Binnen een Comprehension
Wanneer je elementen moet transformeren naar een van twee waarden — in plaats van ze te verwijderen — gebruik dan een ternaire expressie in de uitvoerpositie. De positie telt: de ternaire hoort aan het begin, niet na de iterable.
# 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']# 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 entirelyWanneer Toch een for-lus Gebruiken
List comprehensions zijn voor het bouwen van lijsten. Als je eigenlijk bijwerkingen uitvoert,
gebruik dan een for-lus — geen comprehension. Dit telt verder dan stijl: een comprehension
die zijn resultaat weggooit verspilt geheugen door een lijst te bouwen die niemand gebruikt, en verbergt intentie voor de lezer.
# 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)# 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)- Gebruik een comprehension wanneer je een nieuwe lijst bouwt van een bestaande iterable met een duidelijke expressie en een optioneel filter.
- Gebruik een for-lus wanneer je bijwerkingen uitvoert — I/O, netwerkaanroepen, afdrukken, externe staat muteren.
- Gebruik een for-lus wanneer de transformatielogica meerdere regels, tussenliggende variabelen of opmerkingen nodig heeft om begrepen te worden.
- Gebruik een generatorexpressie wanneer je maar één keer hoeft te itereren of direct doorgeeft aan
sum(),any(),max()— geen lijst nodig in geheugen.
Prestatienoot
List comprehensions zijn betekenisvol sneller dan een equivalente for + append-lus
in CPython. De reden is bytecode: een comprehension compileert naar een speciale LIST_APPEND-opcode
die de attribuutopzoek op list.append bij elke iteratie vermijdt.
De Python-prestatietips-wiki
behandelt dit, en het verschil is typisch 10–40% voor pure Python-werklasten afhankelijk van lijstgrootte.
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))Als je geen gematerialiseerde lijst nodig hebt — je geeft het resultaat door aan sum(),
any(), max(), of itereert het eenmalig — gebruik dan een
generatorexpressie
in plaats daarvan. Het gebruikt constant geheugen ongeacht de invoergrootte, wat telt bij het verwerken van grote CSV-exports
of JSON-payloads in een strakke lus.
Samenvatting
List comprehensions zijn een van die Python-functies die een week ongemakkelijk aanvoelen en dan onmisbaar worden. Het mentale model is eenvoudig: uitvoerexpressie, lusvariabele, iterable, optioneel filter. Houd je daaran en je schrijft leesbare, idiomatische Python. Wanneer de logica complex wordt, is dat het signaal om terug te vallen op een gewone lus — geen mislukking, gewoon het juiste gereedschap voor de klus.
Als je werkt met JSON-data in Python — API-reacties omzetten naar lijsten van waarden, velden extraheren uit records, opzoekdicts bouwen — passen de tools op deze site goed bij wat je net hebt geleerd. Probeer de JSON Formatter om JSON-payloads te inspecteren en mooi af te drukken voordat je de comprehension schrijft die ze verwerkt, of de CSV Formatter om CSV-data te valideren voordat je het parseert naar een lijst van rijen. Voor de volledige taalreferentie zijn de Python-documenten over list comprehensions en PEP 202 (het originele voorstel) het lezen waard.