Python List Comprehensions
List Satellite
List Comprehensions
Transform loops into concise expressions without sacrificing readability.
Syntax
A list comprehension builds a new list by applying an expression to each item of an iterable:
[expression for item in iterable if condition]
The loop version and the comprehension version produce the same list:
names = ['ada', 'grace', 'linus']
# loop version
capitalized = []
for name in names:
capitalized.append(name.title())
# comprehension version
capitalized = [name.title() for name in names]
print(capitalized) # -> ['Ada', 'Grace', 'Linus']
Any expression works on the left side — arithmetic, method calls, function calls:
squares = [n * n for n in range(6)]
print(squares) # -> [0, 1, 4, 9, 16, 25]
Filtering with if
A trailing if keeps only the items that pass the test. Items that fail are skipped entirely, so the result can be shorter than the input:
scores = [82, 45, 91, 60, 38, 77]
passing = [s for s in scores if s >= 60]
print(passing) # -> [82, 91, 60, 77]
Filters combine naturally with a transforming expression:
even_squares = [n * n for n in range(10) if n % 2 == 0]
print(even_squares) # -> [0, 4, 16, 36, 64]
if/else inside the expression
There is a second place an if can appear: before the for, as a conditional expression. This form transforms every item rather than filtering — the output has the same length as the input:
scores = [82, 45, 91, 60, 38, 77]
labels = ['pass' if s >= 60 else 'fail' for s in scores]
print(labels) # -> ['pass', 'fail', 'pass', 'pass', 'fail', 'pass']
The two forms answer different questions:
[s for s in scores if s >= 60]— which items to keep (noelseallowed).['pass' if s >= 60 else 'fail' for s in scores]— what value to produce for each item (elserequired).
You can combine them: [s * 2 if s < 50 else s for s in scores if s > 0] first filters, then transforms.
Nested comprehensions
Two for clauses iterate like nested loops, left to right. The classic use is flattening a matrix:
grid = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]
flat = [value for row in grid for value in row]
print(flat) # -> [1, 2, 3, 4, 5, 6, 7, 8, 9]
Read the clauses in the same order you would write the loop: for row in grid first, then for value in row.
To keep the nested structure and transform each element, nest one comprehension inside another instead:
doubled_grid = [[value * 2 for value in row] for row in grid]
print(doubled_grid) # -> [[2, 4, 6], [8, 10, 12], [14, 16, 18]]
Dict and set comprehensions
The same syntax builds dictionaries with {key: value ...} and sets with {expression ...}:
words = ['apple', 'banana', 'cherry']
lengths = {w: len(w) for w in words}
print(lengths) # -> {'apple': 5, 'banana': 6, 'cherry': 6}
unique_lengths = {len(w) for w in words}
print(unique_lengths) # -> {5, 6} — duplicates collapse automatically
A generator expression uses parentheses and produces items lazily — ideal when you only need to iterate once, such as feeding sum, any, or all:
total = sum(n * n for n in range(6))
print(total) # -> 55
When a plain loop is clearer
Comprehensions shine for a single transform-and-filter step. Reach for a regular loop when:
- The body has side effects — printing, writing files, appending to several lists.
- You need multiple statements — intermediate variables, error handling, logging.
- Nesting gets deep — two
forclauses is the practical limit; three is a puzzle.
totals = []
for order in [120, 40, 75]:
if order > 50:
discounted = order * 0.9
totals.append(round(discounted, 2))
print(f'discount applied to {order}')
# discount applied to 120
# discount applied to 75
print(totals) # -> [108.0, 67.5]
Squeezing that into one comprehension would mean dropping the print and recomputing the discount inline — the loop says it more plainly.
Frequently Asked Questions
Are list comprehensions faster than for loops?
Usually modestly faster, because the append happens in optimized bytecode instead of a repeated method lookup and call. The difference rarely matters compared to the work done per item, so choose the form that reads best and only micro-optimize with measurements.
Can a list comprehension have an else?
Only in the conditional expression before the for, which must have both branches: [x if cond else y for ...]. The trailing if is a filter and cannot take an else — writing [x for x in items if cond else y] is a syntax error.
labels = ['big' if n > 10 else 'small' for n in [4, 40]]
print(labels) # -> ['small', 'big']
How do I flatten a list of lists?
Use two for clauses in one comprehension, written in the same order as the equivalent nested loop. For deeply or irregularly nested data, itertools.chain.from_iterable or a recursive helper is clearer.
grid = [[1, 2], [3, 4]]
flat = [v for row in grid for v in row]
print(flat) # -> [1, 2, 3, 4]