Python comprehensions that read well (and when to stop)
List comprehensions are a superpower until they're a liability. A practical guide to the exact line where clever becomes unreadable — and what to reach for instead.
A Python comprehension is the right tool when it expresses a single, obvious transformation or filter — and the wrong tool the moment it needs two filters, nested loops, or a side effect. Comprehensions are one of the first things that make Python feel like Python, and one of the easiest tools to overuse. Here's a rule of thumb that holds up.
The rule: if you can't read the comprehension out loud in one breath, write the loop.
The good case
A comprehension shines when intent is immediate and the logic is one step:
# Clear: square the evens.
squares = [n * n for n in numbers if n % 2 == 0]
# Clear: build a lookup.
by_id = {user.id: user for user in users}You read these left to right and know exactly what they do. One transform, an optional filter, a clear result — that's the sweet spot.
The line you shouldn't cross
The moment you stack conditionals or nest loops, readability falls off a cliff:
# Don't do this — nobody can scan it.
result = [f(x, y) for x in xs if x > 0 for y in ys if y < x if g(y)]That's a loop pretending to be a one-liner. Write the loop — it's longer and far easier to read, debug, and change:
result = []
for x in xs:
if x <= 0:
continue
for y in ys:
if y < x and g(y):
result.append(f(x, y))A rule that holds up
| Situation | Reach for |
|---|---|
| One transform, optional filter | Comprehension |
| Building a dict or set | Comprehension |
| Two+ filters or nested loops | Plain for loop |
| Side effects (printing, I/O) | Plain for loop |
The side-effect row matters most: a comprehension built only to run print() or append to an external list is misusing the syntax. Comprehensions are for building a collection. If you're not building one, that's a loop.
Why readability is a real skill, not a style opinion
Interviewers and teammates read your code far more often than they run it. A comprehension that needs a second read costs more than the lines it saved. Optimize for the person reading it in six months — usually you. That instinct for "is this still clear?" is exactly the kind of code quality CodeOak's assessment looks at: not just whether your answer passes, but how directly and cleanly you got there.
If you like this lens on writing code that reads well, the SQL version is choosing a CTE vs a subquery for clarity — same principle, different language. Want practice that targets your actual weak spots? Start with the assessment.
FAQ
When should I use a comprehension instead of a for loop in Python?
Use a comprehension when you're building a list, dict, or set from a single transformation with at most one filter. Switch to a plain for loop once you need multiple filters, nested loops, or any side effect like printing or I/O.
Are list comprehensions faster than loops? Comprehensions are often marginally faster because the iteration runs in C, but the difference rarely matters. Choose based on readability first; reach for performance tricks only when profiling shows a real bottleneck.
What's the readability test for a comprehension? If you can read it out loud in one breath and the intent is obvious, keep it. If you stumble or have to re-read, that complexity belongs in a named loop instead.