Excellent.
DAY 23 is where Python’s scalability and elegance really show up. Iterators and generators are not conveniences — they are state machines + lazy execution.
After today, you’ll:
- Think in streams, not lists
- Avoid memory blowups
- Understand how
foractually works at runtime - Ace iterator/generator interview questions
🧠 DAY 23 — Iterators & Generators
(Lazy Evaluation, Protocols & State Machines)
🔑 CORE IDEA OF DAY 23
Iteration in Python is a protocol.
Generators are resumable functions that implement this protocol automatically.
1️⃣ The Iterator Protocol (The Contract)
An iterator must implement:
__iter__()→ returns iterator object__next__()→ returns next value or raisesStopIteration
That’s it.
class CountUp:
def __init__(self, n):
self.n = n
self.i = 0
def __iter__(self):
return self
def __next__(self):
if self.i >= self.n:
raise StopIteration
self.i += 1
return self.i
Now:
for x in CountUp(3):
print(x)
2️⃣ How for REALLY Works (Recap with Precision)
for x in iterable:
body
Is equivalent to:
it = iter(iterable)
while True:
try:
x = next(it)
except StopIteration:
break
body
🧠 Every for loop is try/except-driven.
3️⃣ Iterables vs Iterators (INTERVIEW FAVORITE)
| Term | Meaning |
|---|---|
| Iterable | Has __iter__() |
| Iterator | Has __iter__() + __next__() |
Examples:
iter([1, 2, 3]) # iterator
[1, 2, 3] # iterable
Key rule:
An iterator is exhaustible.
An iterable can create new iterators.
4️⃣ Why Lists Are NOT Iterators
lst = [1, 2, 3]
it = iter(lst)
list(it) # [1, 2, 3]
list(it) # [] (exhausted)
But:
list(lst) # [1, 2, 3] every time
Because:
lst→ iterableiter(lst)→ iterator
5️⃣ Generators — The Shortcut to Iterators
A generator function:
- Contains
yield - Returns a generator object
- Automatically implements iterator protocol
def count_up(n):
i = 0
while i < n:
i += 1
yield i
Usage:
for x in count_up(3):
print(x)
No class needed.
No __next__ written manually.
6️⃣ What yield REALLY Does (Critical Insight)
yield:
- Produces a value
- Pauses function execution
- Saves local state
- Resumes on next
next()
This turns a function into a state machine.
7️⃣ Generator State Machine (Visual)




States:
CREATED → RUNNING → SUSPENDED → RUNNING → DONE
Once DONE → StopIteration forever.
8️⃣ Generator Memory Advantage (WHY THEY MATTER)
Compare:
lst = [x*x for x in range(10_000_000)]
vs
gen = (x*x for x in range(10_000_000))
- List → allocates all memory upfront
- Generator → computes on demand
🧠 This is why generators are essential for:
- Big data
- Streaming
- Pipelines
- ETL logic
9️⃣ Generator Expressions vs List Comprehensions
(x*x for x in range(5)) # generator
[x*x for x in range(5)] # list
Differences:
- Generator is lazy
- List is eager
- Generator uses constant memory
Rule:
Use generator unless you need random access or reuse.
🔟 yield from (Advanced but Powerful)
def chain(a, b):
yield from a
yield from b
Equivalent to:
for x in a:
yield x
for x in b:
yield x
Benefits:
- Cleaner code
- Faster delegation
- Proper exception forwarding
11️⃣ Generator Exhaustion Trap 🔥
g = (x for x in range(3))
print(list(g))
print(list(g))
Output:
[0, 1, 2]
[]
Once consumed, it’s gone.
12️⃣ Exceptions Inside Generators
def gen():
yield 1
raise ValueError("boom")
yield 2
for x in gen():
print(x)
- Exception propagates normally
- Generator terminates
Generators are exception-aware.
🔥 INTERVIEW TRAPS (DAY 23)
Q1
g = (i for i in range(3))
print(next(g))
print(list(g))
✔ Output:
0
[1, 2]
Q2
Why generators are faster for pipelines?
✔ No intermediate lists
✔ Lazy evaluation
✔ Less memory pressure
Q3
Can a generator be restarted?
❌ No
✔ Create a new generator instead
🧠 DAY 23 MENTAL CHECKLIST
When handling large data:
- Can this be streamed?
- Do I really need a list?
- Is one-pass enough?
- Will exhaustion cause bugs?
- Should I document generator behavior?
📝 DAY 23 ASSIGNMENT (MANDATORY)
1️⃣ Predict output (NO RUNNING):
def g():
yield 1
yield 2
x = g()
print(next(x))
print(list(x))
print(list(x))
2️⃣ Explain clearly:
- Difference between iterable and iterator
- How
yieldpreserves state
3️⃣ Design question:
How would you design a memory-efficient log processing pipeline in Python?
(Hint: generators + composition)
🔜 DAY 24 PREVIEW
DAY 24 — Comprehensions & Lazy Evaluation (Performance & Readability Tradeoffs)
You’ll learn:
- List vs set vs dict comprehensions
- Nested comprehensions (and when NOT to use them)
- Generator pipelines
- Readability vs cleverness
When ready, say 👉 “START DAY 24”