Comprehensions & Generators
⚙️ Comprehensions & Generators
yield, which produce values lazily instead of building an entire collection in memory up front.
🎯 Set Comprehensions
The same {expr for item in iterable} shape works for sets too, just with automatic deduplication:
🪆 Nested Comprehensions
Reading order matters: the for clauses read left to right exactly like nested for loops would — for row in matrix is the outer loop, for n in row is the inner one. Written as loops, this would be:
🌊 Generator Expressions
A generator expression looks almost identical to a list comprehension, but with parentheses instead of square brackets — and it computes values one at a time, on demand, rather than building the whole list up front:
For a million items, the list comprehension allocates memory for all million numbers immediately; the generator expression holds almost no memory at all — just enough to know where it left off, computing the next value only when next() (or a for loop) asks for it.
⏸️ Generator Functions and yield
Any function containing yield becomes a generator function — calling it doesn't run the body immediately, it returns a generator object. Each call to next() (which a for loop does automatically) resumes the function right where it last paused, runs until the next yield, and hands back that value — the function's local state (like i here) is preserved between calls, something a regular function can't do at all.
Unlike a list, a generator can only be iterated once — once every value has been produced, it's done, and iterating it again yields nothing. If you need to loop over the same values twice, either use a list/list comprehension instead, or call the generator function again to get a fresh generator object.
Lazy Iteration: Go vs Kotlin vs Python
| Language | Approach |
|---|---|
| Go | No generator syntax — lazy, on-demand value production is typically built manually with goroutines and channels, a heavier-weight tool for the same underlying idea. |
| Kotlin | sequence { yield(value) } — genuinely close to Python's yield, producing a lazy Sequence the same way Python's generator functions produce a lazy generator. |
| Python | yield inside any function, or a generator expression (x for x in ...). |
Kotlin's sequence { yield(...) } is one of the closer direct parallels to a Python feature covered so far in this course — the underlying "pause and resume, producing values lazily" mechanism is essentially the same idea in different syntax.
Set comprehension
{expr for item in iterable} — same shape as list/dict comprehensions.
Nested comprehension
Multiple for clauses read left to right, like nested loops.
Generator expression
(expr for item in iterable) — lazy, memory-efficient, one value at a time.
yield
Pauses a function, hands back a value, and resumes exactly there on the next call.
💻 Coding Challenges
Challenge 1: Unique Word Lengths
Given sentence = "the quick brown fox jumps over the lazy dog", use a set comprehension to find the set of unique word lengths appearing in the sentence.
Goal: Practice a set comprehension combined with .split() from Course 1.
Challenge 2: Flatten and Filter
Given matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]], use a single nested comprehension (with an if condition) to produce a flat list of only the even numbers: [2, 4, 6, 8].
Goal: Practice combining nested comprehensions with a filtering condition in one expression.
Challenge 3: A Fibonacci Generator
Write a generator function fibonacci(n) that yields the first n numbers of the Fibonacci sequence (0, 1, 1, 2, 3, 5, ...), one at a time. Use a for loop to print all values from fibonacci(8).
Goal: Practice writing a generator function with yield that maintains state across calls.
🎯 What's Next
Next chapter: Decorators — function decorators, @property, and functools (wraps, lru_cache).