🔄 Quick Recap (Day 22)
You created charts with Matplotlib (line, bar, scatter) and saved them for reports.
🎯 What You’ll Learn Today
The iterator protocol: iterables vs. iterators,
iter(),next(), andStopIteration.Custom iterators by implementing
__iter__and__next__.Generators: generator functions with
yield,yield from, and generator expressions.Closures: functions that remember values, plus
nonlocalfor stateful inner functions.
📖 Iterables, Iterators, and the Protocol
An iterable is any object you can loop over (e.g., list, tuple, dict).
An iterator is an object with
__iter__()(returns itself) and__next__()(returns the next value or raisesStopIteration).
nums = [10, 20, 30]
itr = iter(nums) # get an iterator
print(next(itr)) # 10
print(next(itr)) # 20
print(next(itr)) # 30
# next(itr) would now raise StopIterationThe for loop calls iter() and repeatedly calls next() under the hood.
📖 Building a Custom Iterator Class
Create your own iterator by defining __iter__ and __next__.
class Countdown:
def __init__(self, n):
self.n = n
def __iter__(self):
return self
def __next__(self):
if self.n <= 0:
raise StopIteration
current = self.n
self.n -= 1
return current
for x in Countdown(3):
print(x)
# 3\n2\n1Use cases: pagination over API pages, streaming sensor readings, or controlled iteration with custom stop conditions.
📖 Generators: Simpler, Lazier Iterators
A generator function uses yield to produce values one at a time, keeping its state between calls.
def evens(limit):
n = 0
while n <= limit:
if n % 2 == 0:
yield n
n += 1
for e in evens(6):
print(e)
# 0 2 4 6Generator Expressions
Compact, lazy sequences using parentheses:
squares = (x * x for x in range(5))
print(next(squares)) # 0
print(next(squares)) # 1Delegating with yield from
def chain(a, b):
yield from a
yield from b
print(list(chain([1, 2], (3, 4)))) # [1, 2, 3, 4]Why generators? Memory efficiency (no big lists), composability (pipelines), and clarity for streaming or large data.
📖 Closures: Functions That Remember
A closure is an inner function that captures variables from an enclosing scope.
def make_multiplier(factor):
def mul(x):
return x * factor # remembers factor
return mul
times3 = make_multiplier(3)
print(times3(5)) # 15Stateful Closures with nonlocal
def make_counter():
count = 0
def inc():
nonlocal count
count += 1
return count
return inc
c = make_counter()
print(c()) # 1
print(c()) # 2Tip (late binding gotcha): In loops, lambda/inner functions capture variables by reference. To freeze a value, use a default arg: lambda x, i=i: (x + i).
🧙♂️ Take the Wand and Try Yourself
Create adv_funcs.py and complete the tasks below.
Custom Iterator
ImplementCountdown(n)that iteratesn, n-1, ..., 1.
class Countdown:
def __init__(self, n):
self.n = n
def __iter__(self):
return self
def __next__(self):
if self.n <= 0:
raise StopIteration
cur = self.n
self.n -= 1
return cur
for i in Countdown(3):
print(i)Expected output:
3
2
1Generators
Writeevens(limit)and a generator expression of their squares. Print the first three squares withnext().
def evens(limit):
n = 0
while n <= limit:
if n % 2 == 0:
yield n
n += 1
squares = (x*x for x in evens(10))
print(next(squares)) # 0
print(next(squares)) # 4
print(next(squares)) # 16Expected output:
0
4
16Closures
Createmake_multiplier(factor)andmake_counter()usingnonlocal.
def make_multiplier(factor):
def mul(x):
return x * factor
return mul
def make_counter():
count = 0
def inc():
nonlocal count
count += 1
return count
return inc
m = make_multiplier(3)
print(m(7)) # 21
c = make_counter()
print(c(), c()) # 1 2Expected output:
21
1 2Bonus: Generator Pipeline
Compose lazy stages to process numbers 1–20 → keep evens → square → print results.
def numbers():
for i in range(1, 21):
yield i
def only_even(seq):
for n in seq:
if n % 2 == 0:
yield n
def square(seq):
for n in seq:
yield n*n
for val in square(only_even(numbers())):
print(val)Expected output (excerpt):
4
16
36
64
100
144
196
256
324
400Run:
python adv_funcs.pyOnce your outputs match, you’ve mastered iterators, generators, and closures—and you’re ready to build efficient, elegant pipelines.
Up next: Day 24: Testing & Debugging — learn unittest/pytest basics and practical debugging strategies.