Level: Advanced | Prerequisite: Week 2 + Intermediate Week 2
Learning Goals
- Implement the iterator protocol (
__iter__,__next__) directly - Delegate to a generator using
yield from - Utilize the powerful functions of
itertools - Coroutine basics (
send)
1. Iterator Protocol
for ... in x is basically iter(x) → next(it) repeatedly.
class Range:
def __init__(self, start, stop):
self.cur = start
self.stop = stop
def __iter__(self):
return self
def __next__(self):
if self.cur >= self.stop:
raise StopIteration
self.cur += 1
return self.cur - 1
print(list(Range(1, 5))) # [1, 2, 3, 4]
2. yield from
Yields another iterable as-is. Useful for flattening.
def chain(*iters):
for it in iters:
yield from it
print(list(chain([1, 2], (3, 4), "ab"))) # [1, 2, 3, 4, 'a', 'b']
3. itertools
A gem of the standard library.
import itertools as it
#Infinite Sequence
counter = it.count(1, 2) # 1, 3, 5, 7, ...
print(list(it.islice(counter, 5))) # [1, 3, 5, 7, 9]
#Combination/Permutation
print(list(it.combinations([1, 2, 3, 4], 2))) # (1,2),(1,3),(1,4),(2,3)...
print(list(it.permutations([1, 2, 3], 2)))
#Group
data = [("a", 1), ("a", 2), ("b", 3), ("b", 4)]
for key, group in it.groupby(data, key=lambda x: x[0]):
print(key, list(group))
#Dictionary Product
print(list(it.product([1, 2], "ab"))) # (1,'a'),(1,'b'),(2,'a'),(2,'b')
#Cumulative
print(list(it.accumulate([1, 2, 3, 4]))) # [1, 3, 6, 10]
4. Coroutine Basics
yield allows two-way communication — inject values with send(value).
def echo():
while True:
x = yield
print(f"Received: {x}")
co = echo()
next(co) #Proceed until the first yield (required)
co.send("hello") #Received: hello
co.send("world") #Received: world
(From Python 3.5+, async/await is more common — next week)
5. Generator vs List
| Generator | List | |
|---|---|---|
| Memory | One item at a time | Entire collection |
| Length | Unknown | len() |
| Reuse | Only once | Multiple times |
| Indexing | Not possible | Possible |
Key Examples (examples/)
| File | Content |
|---|---|
01_iterator.py |
Implement iterator directly |
02_yield_from.py |
yield from |
03_itertools.py |
combinations, groupby, accumulate |
04_coroutine.py |
Send-based coroutine |
⚠️ Common Mistakes
- Iterate over an iterator twice — second time yields empty results; needs to be recreated.
- Omitting StopIteration in
__next__— infinite loop. - Do not sort before groupby — groupby only groups consecutive identical keys.
❓ FAQ
Q1. What is the difference between an iterator and a generator? A. A generator is a type of iterator. Made using yield.
Q2. I heard more-itertools is better than itertools?
A. Not standard, but powerful. Often used in practice.
📝 Exercises (exercises/)
exercise1.md—chunked_lines(path, n)generatorexercise2.md—groupbygroup logs by dateexercise3.md— infinite Fibonacci + islice
Next Week
Week 4. Concurrency (1) - threading