22. π Checkpoint: generators, classes, and types
# 1. [Ch 13] What does this print?
def gen():
yield 1
return 2
g = gen()
print(list(g), list(g))
# 2. [Ch 14] What is wrong with this decorator?
def log_calls(fn):
def wrapper(*args):
print("calling", fn.__name__)
fn(*args)
return wrapper
# 3. [Ch 15] Why does this raise?
from dataclasses import dataclass
try:
@dataclass
class Bag:
items: list = []
except ValueError as e:
print(e)
# 4. [Ch 16] Make `Money(5) + Money(7) == Money(12)` and `sum([Money(1), Money(2)])` work.
Answers
[1] []. Thereturnvalue becomesStopIteration.value, not an item; the generator is exhausted after one pass.- It drops the return value (
return fn(*args)), ignores**kwargs, and lacks@functools.wraps(fn). mutable default <class 'list'> for field items is not allowed: use default_factory. The dataclass protects you from Ch 2.6.- A frozen dataclass with
__add__, plus__radd__that handles0(becausesumstarts from0):def __radd__(self, other): return self if other == 0 else self + other.
Exercises:
- Write a generator
tail(path, n)that yields the lastnlines of a file usingcollections.deque(maxlen=n). - Write a
@rate_limit(calls=5, per=1.0)decorator using a token bucket (HTTPS Ch 9). - Add full type hints to
logstat.pyfrom Ch 12 and makemypy --strictpass. - Write Hypothesis tests for a
parse_duration("1h30m") -> secondsfunction.