Python comprehensions, explained
Comprehensions are one of the first things that make Python code look like Python. They build a collection from an iterable in a single readable expression — but they also get abused into write-only one-liners. This guide covers the variants and, just as importantly, when to stop.
The basic list comprehension
A list comprehension builds a list by transforming each item of an iterable:
squares = [x * x for x in range(5)] # [0, 1, 4, 9, 16]
# equivalent loop:
squares = []
for x in range(5):
squares.append(x * x)
It's both shorter and slightly faster than the loop, because the iteration and appending
run in C. Read it as "give me x * x for each x in the range".
Filtering with a trailing if
Add an if at the end to keep only some items:
evens = [x for x in range(10) if x % 2 == 0] # [0, 2, 4, 6, 8]
This if is a filter — items that fail are skipped entirely.
Conditional transform with a leading if/else
Don't confuse the filter if with a conditional expression. To choose between two values,
the if/else goes before the for (it's a ternary on the output):
labels = ["even" if x % 2 == 0 else "odd" for x in range(4)]
# ['even', 'odd', 'even', 'odd']
Rule of thumb: if at the end filters; if/else at the front transforms.
Dict and set comprehensions
The same syntax builds dicts (with key: value) and sets (with {} and no colon):
squares = {x: x * x for x in range(4)} # {0: 0, 1: 1, 2: 4, 3: 9} — dict
unique = {x % 3 for x in range(10)} # {0, 1, 2} — set
# invert a dict
prices = {"a": 1, "b": 2}
by_price = {v: k for k, v in prices.items()} # {1: 'a', 2: 'b'}
Nested comprehensions
You can iterate multiple loops in one comprehension. The clauses read left to right, in
the same order as nested for loops:
pairs = [(x, y) for x in range(2) for y in range(2)]
# [(0, 0), (0, 1), (1, 0), (1, 1)]
Flattening a list of lists is a common case:
matrix = [[1, 2], [3, 4]]
flat = [n for row in matrix for n in row] # [1, 2, 3, 4]
A nested comprehension (a comprehension inside the output) builds nested structures:
grid = [[0 for _ in range(3)] for _ in range(2)] # 2x3 grid
Note: build the grid this way, not with [[0] * 3] * 2, which shares the inner list across
rows.
When NOT to use a comprehension
Comprehensions are for building a collection. Reach for a plain loop when:
- You're acting for side effects (printing, writing to a file) — don't build a throwaway list just to loop.
- The logic needs
try/except, multiple statements, or several conditions — it stops being readable. - It would be deeply nested — two
forclauses is usually the readability limit.
# Don't do this — comprehension used only for side effects:
[print(x) for x in items] # builds a useless list of Nones
# Just loop:
for x in items:
print(x)
Generator expressions for large data
If you only need to iterate once, a generator expression (parentheses instead of brackets) avoids building the whole list in memory:
total = sum(x * x for x in range(1_000_000)) # lazy — no giant list
Recap
A comprehension builds a collection from an iterable in one expression: a trailing iffilters, while a leading if/else transforms. The same syntax produces dicts
({k: v ...}) and sets ({x ...}), and multiple for clauses read left-to-right for
nesting and flattening. Skip the comprehension when you're looping for side effects or when
the logic gets complex — and use a parenthesised generator expression when the data is
large and you only iterate once.