Comprehensions replace most append-in-a-loop code and are how idiomatic Python transforms data.
A list comprehension describes a new list in terms of an existing iterable: [expression for item in iterable if condition]. It reads as "the expression, for each item, keeping those where the condition holds". It's shorter than a loop with append, and typically a bit faster.
The same syntax builds other containers:
- dict comprehension:
{name: len(name) for name in names} - set comprehension:
{word.lower() for word in words} - generator expression:
(x * x for x in nums)— lazy, produces items on demand; pass it straight to a function likesum(x * x for x in nums)without extra parentheses
The if at the end filters. To transform conditionally, put a conditional expression in the expression position instead: [x if x > 0 else 0 for x in nums].
Multiple for clauses nest left to right, like nested loops written on one line: [(r, c) for r in range(3) for c in range(3)]. One level of nesting is fine; beyond that, a regular loop is clearer.
Comprehensions have their own scope, so the loop variable doesn't leak into the surrounding function. Python 3.12 inlined list, dict and set comprehensions (PEP 709), making them faster without changing that visible behaviour.
Guidelines from experienced reviewers: use comprehensions to build a value, not for side effects ([print(x) for x in xs] creates a useless list); and if it doesn't fit comfortably on one or two lines, write the loop.
Example
nums = [3, -1, 4, -1, 5, -9]
squares = [n * n for n in nums]
positives = [n for n in nums if n > 0]
clamped = [n if n > 0 else 0 for n in nums]
print(positives) # [3, 4, 5]
print(clamped) # [3, 0, 4, 0, 5, 0]
words = ["Apple", "banana", "apple", "Cherry"]
unique = {w.lower() for w in words}
lengths = {w: len(w) for w in words}
print(sorted(unique)) # ['apple', 'banana', 'cherry']
print(lengths["banana"]) # 6
total = sum(n * n for n in nums) # generator expression, no list built
print(total) # 133
grid = [(r, c) for r in range(2) for c in range(3)]
print(grid) # [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]Remember
[expr for x in it if cond]: map and filter in one expression- Use
{k: v ...}for dicts,{x ...}for sets,(x ...)for lazy generators - Prefer a loop for side effects or anything beyond one nesting level
Watch out for
- Putting
elseafter the trailingiffilter — the if/else belongs in the expression part.