The single most "Pythonic" idiom: build a new list, set, or dict from an existing
iterable in one readable line — the rough equivalent of Java's
stream().filter().map().collect(), but built into the language.
Constantly in Python you take one sequence and build another from it — square each number, keep the even ones, map words to their lengths. The literal way is a three-line loop:
A comprehension collapses that exact pattern — create an empty collection, loop, optionally filter, transform, append — into one expression:
It's not just shorter — it signals intent ("I'm building a list by transforming a sequence") and is usually faster than the manual loop. The trick to reading one is that the parts are in a slightly surprising order.
Although you write the output expression first, Python evaluates it left-to-right in execution order:
n.So: source → filter → transform → collect. Build one yourself and watch each item flow
through. Toggle the collection type to see the three siblings — list […],
set {…} (de-duplicates!), and dict {k: v …}.
source = range(8) — each item is kept (✓) or dropped by the filter (✕), then transformed:
The comprehension:
…is exactly this loop:
Result:
( … )Swap the brackets for parentheses and you get a generator expression:
(n * n for n in range(8)). It looks identical but behaves very differently: it doesn't build
the whole collection in memory — it produces items one at a time, on demand (lazy evaluation).
That's why sum(n * n for n in range(1_000_000)) uses almost no memory, while the list version
[n * n for n in range(1_000_000)] would materialise a million numbers first. Reach for a
generator when you only need to iterate once (feeding sum, any,
max, a for loop) and the data is large.
Comprehensions shine for the map/filter shape: one source, a simple condition, a simple transform. They're declarative and fast. But they're easy to abuse:
for clauses can be fine
([x for row in grid for x in row] flattens), but three levels or a tangle of
ifs is unreadable — use a real loop.for loop — a comprehension that throws its list away is
a code smell.( … ) when the sequence is large and consumed once.[…] builds a list, {…} a set (de-duped) or dict
({k: v}), (…) a lazy generator. Use them for clear map/filter work; fall back to
a loop when logic gets gnarly or you only want side effects.