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

1. You shouldn't have to collect values in fibonacci function if you yield them 2. Instead of checking length of array just add start values into `values` array before `while` 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