Back to Blog

Python List Comprehensions — 5 Patterns You'll Actually Use

Celest KimCelest Kim

Video: Python List Comprehensions — 5 Patterns You'll Actually Use by CelesteAI

Take the quiz on the full lesson page
Test what you've read · interactive walkthrough

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.

Ready? Take the quiz on the full lesson page →
Test what you've learned. Watch the lesson and try the interactive quiz on the same page.