Lesson 11 of 34 · 0 of 2 exercises solved
Comprehensions & Generators
build a new , dict or from an existing one in a single line. Use them for simple transformations and switch to a regular when the logic gets complicated.
# List comprehension — [expression for item in iterable if condition]
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
squares = [n**2 for n in numbers]
print(squares) # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
evens = [n for n in numbers if n % 2 == 0]
print(evens) # [2, 4, 6, 8, 10]
even_squares = [n**2 for n in numbers if n % 2 == 0]
print(even_squares) # [4, 16, 36, 64, 100]
# Nested comprehension (flatten a 2D list)
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [x for row in matrix for x in row]
print(flat) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
# Dict comprehension
words = ["hello", "world", "python"]
lengths = {word: len(word) for word in words}
print(lengths) # {'hello': 5, 'world': 5, 'python': 6}
# Set comprehension
dupes = [1, 2, 2, 3, 3, 3, 4]
unique = {x**2 for x in dupes}
print(unique) # {1, 4, 9, 16} (order not guaranteed)
# Conditional expression (ternary)
labels = ["even" if n % 2 == 0 else "odd" for n in range(1, 6)]
print(labels) # ['odd', 'even', 'odd', 'even', 'odd']# Generator expressions — lazy, memory-efficient
# Use () instead of []
gen = (n**2 for n in range(1_000_000)) # creates no list yet
print(next(gen)) # 0
print(next(gen)) # 1
print(sum(gen)) # computes on the fly
# Generator function — yield instead of return
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
fib = fibonacci()
print([next(fib) for _ in range(10)]) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
# Generator with yield from
def chain(*iterables):
for it in iterables:
yield from it
print(list(chain([1, 2], [3, 4], [5]))) # [1, 2, 3, 4, 5]
# itertools — powerful combinator utilities
import itertools
print(list(itertools.islice(fibonacci(), 8))) # first 8 Fibonacci numbers
print(list(itertools.product([0,1], repeat=3))) # all 3-bit binary combos
print(list(itertools.combinations("ABCD", 2))) # C(4,2) = 6 pairs
pairs = list(itertools.zip_longest([1,2,3], [4,5], fillvalue=0))
print(pairs) # [(1, 4), (2, 5), (3, 0)]Common question
Is a comprehension just a shorter loop?
A comprehension is an expression that builds a collection from an by mapping and filtering: [f(x) for x in xs if p(x)]. Because it is an expression, you can assign it, return it or pass it directly, and it tells the reader this is a pure transformation with no side effects. Dict and set comprehensions build those types; a generator expression gives the same values lazily.
Use comprehensions for transformations that fit on one readable line. Use loops when there are side effects, several outputs, or logic that deserves a name. Prefer a generator expression when the result is used once by sum, max, any, all or a join.
Source: The Zen of Python: "Beautiful is better than ugly. Simple is better than complex."
Common mix-ups
Each of these sounds reasonable. Think about why it is wrong, then open the explanation.
A comprehension does the same work as a loop with append, so it's only a matter of taste.
If one comprehension is good, nesting three of them is better.
A generator expression is just a list comprehension with round brackets.
Practice
Rewrite the loop as a single list comprehension that collects the squares of the odd numbers from 1 to 20.
Squares of odds
squares = []
for n in range(1, 21):
if n % 2 == 1:
squares.append(n * n)
print(squares)
Press Run to try your code, then Check answer to grade it.Build two things with comprehensions: lengths, a dict mapping each word to its length, and flat, a single flat list of every number in the nested matrix.
Dict and nested comprehensions
words = ["python", "is", "wonderful"]
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
lengths = {}
flat = []
print(lengths)
print(flat)
Press Run to try your code, then Check answer to grade it.Check yourself
What is the difference between [x * 2 for x in data] and (x * 2 for x in data)?
Tip: use the ← and → keys to move between lessons.