At the end of the article below, I wrote that the generator should be containerized.
-Generate Fibonacci numbers with Python closures, iterators and generators
This time I will explain this.
First, create a generator that generates the Fibonacci number as before. In the comment of the previous article, I was taught a sophisticated writing method, so I will use that.
Python
#generator
def fibonacci_generator():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
#Containerized generator
class FibonacciGenerator(object):
def __iter__(self):
a, b = 0, 1
while True:
yield a
a, b = b, a + b
These output the same result.
Python
for fib in fibonacci_generator():
if fib > 1000:
break
print fib,
print
for fib in FibonacciGenerator():
if fib > 1000:
break
print fib,
result
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
Note that the above two for statements differ only in the generator. It's tedious to write this code every time the generator changes, so let's make it a function.
Python
def print_fib(fib_iterator, limit = 1000):
for fib in fib_iterator:
if fib > limit:
break
print fib,
print
print_fib(fibonacci_generator())
print_fib(FibonacciGenerator())
result
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
It was neat to create a function that receives a generator.
Now let's modify this function to display the contents of the generator twice.
Python
def print_fib_twice(fib_iterator, limit = 100):
print "[1]",
for fib in fib_iterator:
if fib > limit:
break
print fib,
print " [2]",
for fib in fib_iterator:
if fib > limit:
break
print fib,
print
print_fib_twice(fibonacci_generator())
print_fib_twice(FibonacciGenerator())
result
[1] 0 1 1 2 3 5 8 13 21 34 55 89 [2]
[1] 0 1 1 2 3 5 8 13 21 34 55 89 [2] 0 1 1 2 3 5 8 13 21 34 55 89
How, the generator that is not containerized does not display the contents of the second time. The reason for this is that if you use the generator as it is, the same one will be reused the first time and the second time. The containerized generator will create a new generator for each loop. This is easy to understand if you try the following.
Python
def print_fib_twice2(fib_iterator, limit1, limit2):
print "[1]",
for fib in fib_iterator:
if fib > limit1:
break
print fib,
print " [2]",
for fib in fib_iterator:
if fib > limit2:
break
print fib,
print
print_fib_twice2(fibonacci_generator(), limit1 = 100, limit2 = 400)
print_fib_twice2(FibonacciGenerator(), limit1 = 100, limit2 = 400)
result
[1] 0 1 1 2 3 5 8 13 21 34 55 89 [2] 233 377
[1] 0 1 1 2 3 5 8 13 21 34 55 89 [2] 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
In this way, if a generator is passed to a function that is used multiple times internally, it will cause an unexpected error. Therefore, it is generally safer to containerize it.
On the contrary, when writing a function that receives an iterator, it is better to check whether it is containerized. You can determine if an iterator is containerized by applying ʻiter ()` twice and checking if different iterators are returned.
Python
def print_fib_twice3(fib_iterator, limit = 100):
if iter(fib_iterator) is iter(fib_iterator):
raise TypeError("Must supply a container")
print "[1]",
for fib in fib_iterator:
if fib > limit:
break
print fib,
print " [2]",
for fib in fib_iterator:
if fib > limit:
break
print fib,
print
Python
print_fib_twice3(fibonacci_generator())
result
TypeError: Must supply a container
Python
print_fib_twice3(FibonacciGenerator())
result
[1] 0 1 1 2 3 5 8 13 21 34 55 89 [2] 0 1 1 2 3 5 8 13 21 34 55 89
This will prevent unintended behavior.
Enjoy!
Recommended Posts