We saw how take a simple function and using callbacks make it more general. We also saw how to create an iterator to make our code more straight-forward. This time we are going to see how to convert the plain function into a generator that, after understanding how generators work, will seem to be the most obvious solution.
Plain function
As a reminder let's see the original plain Fibonacci function we started with, that we had to change to hold our hard-coded condition, or in the more flexible case that we had to make it execute a callback function.
examples/python/fibonacci_function.py
#!/usr/bin/env python
from __future__ import print_function
def fibonacci():
values = []
while(True):
if len(values) < 2:
values.append(1)
else:
values = [values[-1], values[-1] + values[-2]]
if __name__ == '__main__':
fibonacci()
Generator
examples/python/fibonacci_generator.py
#!/usr/bin/env python
from __future__ import print_function
def fibonacci():
values = []
while(True):
if len(values) < 2:
values.append(1)
else:
values = (values[-1], values[-1] + values[-2])
yield values[-1]
for f in fibonacci():
if f % 17 == 0:
print(f)
break
if f > 10000:
break
The example with the generator is almost exactly the same as the plain function, and the way we can use it is exactly the same as we use the iterator
The only addition in the generator implementation of the fibonacci
function is that it calls yield
every time it calculates one of
the values. This call pauses the execution of the fibonacci
function
and returns the command to the calling entity together with the value passed
to the yield
statement.
For the first iteration of the for
loop the fibonacci
function
will start running from its first statement assigning an empty list to values
.
When it encounters the yield
statement it will return the value in values[-1]
that will be assigned to f
of the for
loop and it will let the for
loop execute its code. There we can put any condition to break-out from the loop.
If we don't break
on the first iteration then on the subsequent
iterations of the for
loop the fibonacci
function will continue
from the exact state where it was paused. Meaning the content of the values
will be exactly the same as it was left, and the first statement to be executed will be the one
immediately after the yield
statement which, in this case, will be checking
if True
is still true in the while(True):
statement.
So from the outside the fibonacci
function will behave just as the
Fibonacci iterator does which
makes our code simple.
Comments
- You shouldn't have to collect values in fibonacci function if you yield them
- Instead of checking length of array just add start values into
values
array beforewhile
cycle
def fib_gen(): a = 1 b = 1 yield b while True: yield b a,b = b,a+b
fibonacci = fib_gen() for item in xrange(10000): fib_number = fibonacci.next() if fib_number % 17 == 0: print fib_number break
instead of using xrange, or range in Python 3, alongside with next(), you could do the following in your for loop:
k,n=0,10000 for i fib_gen(): k+=1 if i % 17 == 0: print(i) break if k==n: break