Advanced Comprehensions
Nested loops, conditionals, dict and set comprehensions — plus when to stop and write a loop.
You have met the basic list comprehension. Here we push it: multiple loops, filtering, transforming into dicts and sets, and — importantly — knowing when a comprehension has become too clever.
Nested loops read top-to-bottom
Multiple for clauses run left to right, exactly like nested loops written out:
pairs = [(x, y) for x in range(2) for y in range(2)]
print(pairs) # [(0, 0), (0, 1), (1, 0), (1, 1)]
# Filter with if; transform with a conditional expression
labels = ['even' if n % 2 == 0 else 'odd' for n in range(4)]
print(labels) # ['even', 'odd', 'even', 'odd']Dict and set comprehensions
Swap the brackets: {k: v for ...} builds a dict, {expr for ...} builds a set (deduplicating as it goes). Inverting a dictionary or indexing a list by a key becomes a one-liner.
Know when to stop
A comprehension should fit in your head at a glance. Two for clauses and one if is usually the ceiling. Beyond that — or when you need a try, or an intermediate variable used twice — a plain loop is more readable, and readability wins.
Example
When to use it
- A matrix transposer uses a nested list comprehension to swap rows and columns in one expression.
- An inverted index is built as a dict comprehension mapping each word to its list of document IDs.
- A pipeline switches from a list comprehension to a generator expression when processing millions of records to save memory.
More examples
Nested list comprehension
Transposes a 3x3 matrix with a nested comprehension: the outer iterates columns, inner iterates rows.
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
transposed = [[row[i] for row in matrix] for i in range(3)]
print(transposed)
# [[1, 4, 7], [2, 5, 8], [3, 6, 9]]Dict comprehension with condition
Builds a filtered dict of only in-stock items using a dict comprehension with an if clause.
inventory = {'apple': 0, 'banana': 5, 'cherry': 0, 'date': 3}
in_stock = {k: v for k, v in inventory.items() if v > 0}
print(in_stock)
# {'banana': 5, 'date': 3}Generator expression vs list comprehension
Compares memory usage: a list comprehension allocates all values, a generator expression is constant size.
import sys
big = range(1_000_000)
list_comp = [x ** 2 for x in big] # materialises all 1M items
gen_expr = (x ** 2 for x in big) # lazy, computes on demand
print(sys.getsizeof(list_comp)) # ~8 MB
print(sys.getsizeof(gen_expr)) # ~200 bytes
Discussion