Generators, Iterators, and Decorators Exercises
Objectives
This chapter introduces no new concepts: it’s a chance to put generators, iterators, and decorators into practice.
Part I: Generators
get_next_multiple
Accepts a number and returns a generator that yields successive multiples of it, forever.
gen_multiple_of_two = get_next_multiple(2)
next(gen_multiple_of_two) # 2
next(gen_multiple_of_two) # 4
next(gen_multiple_of_two) # 6
next(gen_multiple_of_two) # 8
gen_multiple_of_thirteen = get_next_multiple(13)
next(gen_multiple_of_thirteen) # 13
next(gen_multiple_of_thirteen) # 26
next(gen_multiple_of_thirteen) # 39
next(gen_multiple_of_thirteen) # 52
is_prime
Accepts a number and returns True or False depending on whether it’s prime.
is_prime(11) # True
is_prime(122) # False
get_next_prime
Returns a generator that yields the next prime number each time, capping out at 1000.
gen = get_next_prime()
next(gen) # 2
next(gen) # 3
next(gen) # 5
Part II: Decorators
double_result
A decorator that doubles the return value of the function it wraps.
def add(a, b):
return a + b
add(5, 5) # 10
@double_result
def add(a, b):
return a + b
add(5, 5) # 20
only_even_parameters
A decorator that only calls the wrapped function if every argument is even. Otherwise it returns "Please only use even numbers!".
@only_even_parameters
def add(a, b):
return a + b
add(5, 5) # "Please only use even numbers!"
add(4, 4) # 8
@only_even_parameters
def multiply(a, b, c, d, e):
return a * b * c * d * e
Callback: once → @run_once
Back in the functions module, you wrote a once function that took another function and returned a version of it that could only run one time. Now that you know how decorators work, come back and rewrite it as a proper decorator, run_once:
@run_once
def add(a, b):
return a + b
add(2, 2) # 4
add(2, 20) # None
Part III: More Practice
sum_index
Accepts a list or tuple and returns the sum of all its values. Bonus: make it accept a variable number of individual arguments too, not just a single list.
sum_index([1, 2, 3, 4]) # 10
zip
Research the built-in zip() function: what does it do, and when would you reach for it? Try it out:
names = ["Erin", "Jordan", "Maya"]
scores = [90, 85, 78]
list(zip(names, scores))
# [('Erin', 90), ('Jordan', 85), ('Maya', 78)]
Try It
- Implement
get_next_multipleandis_prime, then useis_primeto help buildget_next_prime. - Implement
double_resultandonly_even_parameters, and test both against the examples above. - Rewrite
onceas@run_once, confirming it still only lets the wrapped function run a single time.
Recap
You can now write generator functions with yield, understand the relationship between generators and iterators, and write your own decorators, including revisiting earlier code to decorate it properly. That’s the toolkit Module 8 set out to build.
Next module: web scraping, pulling data from live web pages.