List comprehensions confuse beginners not because they are complex but because they are written in the opposite order from how most people think about them.
Once you learn to read them in the right order, they become faster to understand than the equivalent for loop.
The Reading Order
Do not read a comprehension left to right. Read it in this order:
- The
forpart first: where do the values come from? - The
ifpart second: which values are included? - The expression first: what happens to each included value?
result = [x * 2 for x in range(10) if x % 3 == 0]
Step 1 (for): x takes values 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
Step 2 (if): keep only x where x % 3 == 0: that gives 0, 3, 6, 9
Step 3 (expression): multiply each by 2: 0, 6, 12, 18
Output: [0, 6, 12, 18]
Dictionary Comprehensions Work the Same Way
scores = {"Alice": 85, "Bob": 42, "Carol": 91, "Dave": 67}
passed = {name: score for name, score in scores.items() if score >= 70}
print(passed)
Step 1 (for): name and score take each key-value pair
Step 2 (if): keep only pairs where score is 70 or above
Step 3 (expression): create a new dict with those same pairs
Output: {'Alice': 85, 'Carol': 91, 'Dave': 67}
Set Comprehensions Remove Duplicates Automatically
words = ["hello", "world", "hello", "python", "world", "code"]
unique_lengths = {len(word) for word in words}
print(unique_lengths)
Output: {4, 5, 6} (order may vary, sets are unordered)
"hello" has length 5, "world" has length 5, "python" has length 6, "code" has length 4. The set removes duplicate lengths automatically.
The Nested Comprehension
This is where most people give up, but the reading order works here too.
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [num for row in matrix for num in row]
print(flat)
Read the for clauses left to right, outer to inner:
First for: row takes each sublist: [1,2,3], [4,5,6], [7,8,9]
Second for: num takes each element in the current row
Expression: include num as-is
Output: [1, 2, 3, 4, 5, 6, 7, 8, 9]
The Interview Version
pairs = [(x, y) for x in range(3) for y in range(3) if x != y]
print(pairs)
Outer for: x takes 0, 1, 2
Inner for: y takes 0, 1, 2 for each x
If: exclude pairs where x equals y
Pairs where x != y:
(0,1), (0,2), (1,0), (1,2), (2,0), (2,1)
Output: [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
Generator Expressions: The Memory-Efficient Version
import sys
list_comp = [x**2 for x in range(1000)]
gen_expr = (x**2 for x in range(1000))
print(sys.getsizeof(list_comp))
print(sys.getsizeof(gen_expr))
Output (approximate):
8856
104
The list comprehension stores all 1000 squared values in memory. The generator expression stores only the recipe for generating them, computing each value on demand. For large sequences, the generator uses a fraction of the memory.
For more comprehension and output prediction practice, visit PyCodeIt.
Top comments (0)