# Iterators and Generators in Python

If you have written some code in Python, something more than the simple “Hello World” program, you have probably used iterable objects. Iterable objects are objects that conform to the *Iteration Protocol* and can hence be used in a loop.

For example:

```
1for i in range(50):
2 print(i)
```

In this example, the `range(50)`

is an iterable object that provides, at each iteration, a different value that is assigned to the `i`

variable.

Quite easy, but what if we would like to create an iterable object ourselves?

## The iteration protocol

Creating an iterable object in Python is as easy as implementing the *iteration protocol*.
Let’s pretend that we want to create an object that would let us iterate over the *Fibonacci sequence*. The Fibonacci sequence is a sequence of integer numbers characterized by the fact that every number after the first two is the sum of the two preceding ones. So the sequence starts with 0 and 1 and then each number that follows is just the sum of the two previous numbers in the sequence. So the third number is 1 (0+1), the fourth is 2 (1+1), the fifth is 3 (1+2), the sixth is 5 (2+3) and so on.

Enough said, let’s the code talk:

```
1class fibonacci:
2
3 def __init__(self, max=1000000):
4 self.a, self.b = 0, 1
5 self.max = max
6
7 def __iter__(self):
8 # Return the iterable object (self)
9 return self
10
11 def next(self):
12 # When we need to stop the iteration we just need to raise
13 # a StopIteration exception
14 if self.a > self.max:
15 raise StopIteration
16
17 # save the value that has to be returned
18 value_to_be_returned = self.a
19
20 # calculate the next values of the sequence
21 self.a, self.b = self.b, self.a + self.b
22
23 return value_to_be_returned
24
25 def __next__(self):
26 # For compatibility with Python3
27 return self.next()
28
29
30if __name__ == '__main__':
31 MY_FIBONACCI_NUMBERS = fibonacci()
32 for fibonacci_number in MY_FIBONACCI_NUMBERS:
33 print(fibonacci_number)
```

As you can see, all we’ve done has been creating a class that implements the iteration protocol. This protocol consists in two methods:

- the
`.__iter__()`

method that returns the object we would iterate over - the
`.__next__()`

method that is called automatically on each iteration and that returns the value for the current iteration.

Please note that the protocol in Python 2 is a little different and the `.__next__()`

method is called just `.next()`

so it is quite common to use the old Python 2 style method to generate the value and then create the Python 3 style method to simply return the value generated by the former one, so as to have code that can works both with Python 2 and Python 3.
Generators

Generators in Python are just another way of creating iterable objects and are usually used when you need to create iterable object quickly, without the need of creating a class and adopting the iteration protocol. To create a generator you just need to define a function and then use the `yield`

keyword instead of `return`

.

So, the Fibonacci sequence in a generator could be something like this:

```
1# mario.py
2def fibonacci(max):
3 a, b = 0, 1
4 while a < max:
5 yield a
6 a, b = b, a+b
7
8if __name__ == '__main__':
9 # Create a generator of fibonacci numbers smaller than 1 million
10 fibonacci_generator = fibonacci(1000000)
11
12 # print out all the sequence
13 for fibonacci_number in fibonacci_generator:
14 print(fibonacci_number)
```

Yes, so simple! Now, run it and see the Fibonacci sequence generated right away:

```
$ python mario.py
0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025
121393
196418
317811
514229
832040
```

Please note that once we have consumed the generator, we can’t use it anymore because generators in Python can’t be rewound.

So, if after the code above we tried to print out all the sequence again, we won’t get any values.

```
1 # since the sequence is over, we will not get any value here
2 for fibonacci_number in fibonacci_generator:
3 print(fibonacci_number)
```

And if you need to use the generator again, you have to call the generator function again

```
1 # So, if you need to use the generator again... recreate it!
2 fibonacci_generator = fibonacci(1000000)
3
4 # Ok, let's list 'em again
5 for fibonacci_number in fibonacci_generator:
6 print(fibonacci_number)
```

Now, if you can, take some time to debug the generator code above and look at how the values are generated and returned. You will find out that the values are generated in a lazy way, just when they need to be generated and then they are returned by the yield statement as it’s hit. Hence, the line after the yield is executed just when it needs to be executed when the next value is requested.

About debugging the code I have to say that one of the best tool to write and debug Python code I know is from Microsoft and it’s Visual Studio Code. It’s really good and available for Windows, macOS and Linux for free. Playing with iterable objects

Iterable objects give you a lot of possibilities. For example, if you need to create a list from the previous generator you can simply do:

```
1 my_fibonacci_list = list(fibonacci(100000))
2 print("My fibonacci list: {0}".format(my_fibonacci_list))
```

and you will get:

```
My fibonacci list: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025
```

Another way of creating a list from an iterable object is by using list comprehension that allows you to create a list in a very natural way, specifying also which elements to choose for the list. For example, if you need to create a list with only the odd Fibonacci numbers you can do:

```
1 fibonacci_odds_list = [x for x in fibonacci(100000) if x%2!=0]
2 print("The odds number are: {0}".format(fibonacci_odds_list))
```

and you’ll get:

```
The odds number are: [1, 1, 3, 5, 13, 21, 55, 89, 233, 377, 987, 1597, 4181, 6765, 17711, 28657, 75025]
```

And you can use them also for all the functions based on iterables, like `sum()`

, `max()`

, `min()`

and so on, like this:

```
1 print("The min number is: {0}".format(min(fibonacci(1000000))))
2 print("The max number is: {0}".format(max(fibonacci(1000000))))
3 print("The sum of is: {0}".format(sum(fibonacci(1000000))))
```

and running this example you’ll get:

```
The min number is: 0
The max number is: 832040
The sum is: 2178308
```

… or for functional programming functions like `map()`

and `reduce()`

… but this is another story for a future article.

Happy Pythoning! D.

This article has been written in loving memory of one of the most amazing human being I’ve ever known and that taught me a lot. **Thank you Mario T.** rest in peace.