The chalkboard needs every drink with its fifty-cent oat-milk upgrade. A loop works; a single expression reads like the sentence Maya would say out loud. When is the comprehension the clearer choice?
A comprehension creates a new list, set, or dictionary from an iterable in one expression. It is not automatically better than a loop, but it is often clearer when the transformation is short and the result is the only thing you need. You will also see comprehension scope, nested for order, and one-shot generators.
Transform every item
The expression before for is applied to each item. The result is a new list; the original list is unchanged. Keep the expression short enough that a reader can see both the input and the output.
nums = [1, 2, 3]
doubled = [n * 2 for n in nums]
print(doubled)Mapping values
Filter with if
An if clause at the end keeps only the values that pass the test. Put the condition after the for clause. If the logic needs several branches, a normal loop may be easier to read.
nums = [1, 2, 3, 4]
even = [n for n in nums if n % 2 == 0]
print(even)Filtering values
Build a dictionary
A dictionary comprehension uses a key expression and a value expression separated by a colon. Iterate over items when you need both the existing key and value.
prices = {"latte": 4.5, "tea": 3.0}
expensive = {name: price for name, price in prices.items() if price >= 4}
print(expensive)A filtered dictionary
The comprehension variable stays local
In Python 3, a comprehension has its own scope. Reusing n outside does not read the last value from the comprehension, which prevents accidental leakage. The result collection is the only value that escapes.
n = 99
squares = [n * n for n in range(4)]
print(squares)
print(n)The loop variable does not leak
Nested for clauses read left to right
A nested comprehension is a flattened pair of loops. The first for is outer; the second runs for each outer item. Filters belong after the loops they filter. If the order is not obvious, use a named loop instead.
rows = [[1, 2], [3, 4]]
flat = [value for row in rows for value in row]
pairs = [(row, value) for row in rows for value in row if value % 2 == 0]
print(flat)
print(pairs)Outer loop first, filter last
A generator expression is consumed once
Parentheses create a lazy generator. sum() consumes it; a second pass sees an exhausted iterator and returns zero. Use a list when you need repeated passes, or rebuild the generator deliberately.
numbers = (n * n for n in range(4))
print(sum(numbers))
print(sum(numbers))
print(list(n * n for n in range(4)))Stream once, or store the values
Key points
- A comprehension returns a new collection.
- The for clause supplies values; the if clause filters them.
- Dictionary comprehensions use key: value before for.
- Prefer a normal loop when the logic becomes hard to scan.
- A generator streams once; a list supports repeated passes.
Common mistakes
- Putting the if filter before the for clause.
- Using a comprehension only for side effects such as print.
- Building a huge nested comprehension that no one can read.
- Passing the same generator to two consumers and expecting both to see every item.
Build a list of squares for the numbers 1 through 5, then keep only the squares above 10.
Try it yourself
Change the values, predict the output, then run the code. The Python runtime is downloaded the first time you press Run, so the first run can take a few seconds.
Practice this lesson
12 questions with immediate explanations.