Python List Comprehensions — 5 Patterns You'll Actually Use
Video: Python List Comprehensions — 5 Patterns You'll Actually Use by CelesteAI
A list comprehension is a one-line for loop that builds a list. Same algorithm, fewer characters, and faster because more of the loop runs in C instead of Python bytecode.
This post walks the five patterns you’ll meet in real code, then the timing on 100,000 items, then the one case where a regular for loop is still the right call.
The basic shape
[expression for item in iterable]
The expression on the left produces each output element. The for clause on the right iterates the source. An optional if clause at the end filters. That’s the whole syntax.
1. Map — transform every element
nums = list(range(10))
squares = [x * x for x in nums]
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
The for-plus-append equivalent is four lines:
squares = []
for x in nums:
squares.append(x * x)
Same algorithm. The comprehension is shorter, faster, and more readable for trivial transformations.
2. Filter — keep elements that pass
evens = [x for x in nums if x % 2 == 0]
# [0, 2, 4, 6, 8]
The if clause goes at the end. The expression on the left is just x because we’re keeping the original value, not transforming it.
3. Map and filter combined
pos_squares = [x * x for x in nums if x > 3]
# [16, 25, 36, 49, 64, 81]
Same shape, both clauses in one expression. The if filters first; the expression on the left transforms what survives.
4. Flatten — nested for clauses
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [item for row in matrix for item in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
Multiple for clauses read left to right like nested for loops. The outer loop comes first, the inner second. Get this backwards and the comprehension throws NameError because the inner variable doesn’t exist yet.
5. Dict and set comprehensions
Same syntax, different brackets. With a colon between the key and value expression, you get a dict. Without a colon, you get a set.
words = ["python", "lambda", "comprehension"]
lengths = {w: len(w) for w in words}
# {"python": 6, "lambda": 6, "comprehension": 13}
unique_mods = {x % 3 for x in range(100)}
# {0, 1, 2}
Speed — comprehensions beat for-plus-append by ~2x
N = 100_000
# for + append
out = []
for i in range(N):
out.append(i * 2)
# ~3.8 ms
# comprehension
out = [i * 2 for i in range(N)]
# ~1.8 ms (2.1x faster)
Same complexity. Same algorithm. The comprehension wins because:
1. CPython knows the result is a list being built in one go and allocates more efficiently.
2. The loop body runs inside CPython’s evaluation loop, skipping the per-iteration overhead of a Python-level .append method call.
This is the same overhead pattern that makes iterrows slow in pandas. (Ep 9 measures it in DataFrame land.)
When to use a for loop instead
A comprehension is one expression. The moment your loop body needs multiple statements, side effects, or break/continue, switch to a regular for loop. Don’t fight the syntax.
def process_with_logging(items):
out = []
for item in items:
cleaned = item.strip().lower()
if not cleaned:
continue # ← break/continue read naturally in a loop
print(f" processing: {cleaned}") # ← side effect (logging)
out.append(cleaned)
return out
You could force this into a comprehension with walrus :=, conditional expressions, and a separate logging side-effect. But it would be unreadable. The whole point of comprehensions is that the transformation fits on one line and reads cleanly. When it doesn’t, the for loop is the right tool.
The decision
| Situation | Tool |
|---|---|
| Trivial map | [expr for x in xs] |
| Filter | [x for x in xs if cond] |
| Map + filter | [expr for x in xs if cond] |
| Flatten | [item for row in xs for item in row] |
| Dict from iterable | {k: v for k, v in pairs} |
| Multi-statement, side effects, break/continue | for loop |
Want the deeper dive?
Common Questions Ep 9 — Why Is My pandas Loop So Slow? measures the same Python-loop-overhead pattern on pandas DataFrames (where the overhead is 100×–1000×, not 2×). Same lesson at scale.