Infinite sequences in Python – Impressive Fibonacci

infinite sequences python fibonacci featured image

Did you know that you can create an infinite sequence without having memory problems and still consume this sequence in any way you want? Today we will see a specific function of the itertools standard library module, the islice, and see how can it be used to create the Fibonacci infinite sequence in Python.

Whenever you hear about a feature of your favorite programming language (or the language that pays your bills…) take a look at the documentation. The documentation of the itertools module describes it as:

This module implements a number of iterator building blocks inspired by constructs from APL, Haskell, and SML. Each has been recast in a form suitable for Python. The module standardizes a core set of fast, memory efficient tools that are useful by themselves or in combination. Together, they form an “iterator algebra” making it possible to construct specialized tools succinctly and efficiently in pure Python. (…)

Note that the documentation mentions iterators. So, if you are not very familiar with the concept, I recommend reading this article here on the site where I explain in detail what it is all about. Then come back to this article here :-).

As for the islice method, the documentation describes it as:

itertools.islice(iterable, stop) or itertools.islice(iterable, start, stop[, step]). Make an iterator that returns selected elements from the iterable. (…)

A very succinct description. Let’s start by importing the function:

from itertools import islice

Creating our infinite sequence

Now let’s create our infinite sequence. If you have already read the article on generators you know that it is perfectly possible to create such sequences. Just create a generator and yield the values on demand:

def positive_even_numbers():
    value = 0
    while True:
        yield value
        value += 2

Notice that the generator will yield positive integers starting from zero whenever requested. Let’s quickly remember the behavior of such a generator. First, seeing that Python recognizes it as a generator:

positive_even_numbers()

Let’s assign it to a variable so that we can consume it via next calls:

p = positive_even_numbers()
next(p)
0
next(p)
2
next(p)
4

And we could go on indefinitely. Let’s now understand what islice does.

Consuming the sequence in a controlled way

Let’s take a closer look at the function description:

help(islice)
Help on class islice in module itertools:

class islice(builtins.object)
 |  islice(iterable, stop) --> islice object
 |  islice(iterable, start, stop[, step]) --> islice object
 |  
 |  Return an iterator whose next() method returns selected values from an
 |  iterable.  If start is specified, will skip all preceding elements;
 |  otherwise, start defaults to zero.  Step defaults to one.  If
 |  specified as another value, step determines how many values are
 |  skipped between successive calls.  Works like a slice() on a list
 |  but returns an iterator.
 |  
 |  Methods defined here:
 |  
 |  __getattribute__(self, name, /)
 |      Return getattr(self, name).
 |  
 |  __iter__(self, /)
 |      Implement iter(self).
 |  
 |  __next__(self, /)
 |      Implement next(self).
 |  
 |  __reduce__(...)
 |      Return state information for pickling.
 |  
 |  __setstate__(...)
 |      Set state information for unpickling.
 |  
 |  ----------------------------------------------------------------------
 |  Static methods defined here:
 |  
 |  __new__(*args, **kwargs) from builtins.type
 |      Create and return a new object.  See help(type) for accurate signature.

Notice that there are two signatures. The first is islice(iterable, stop). So let’s pass our iterable (the generator) and a stop value, for example, 10:

islice(p, 10)

It really is an iterator. Let’s then consume it, passing it to a tuple:

tuple(islice(p, 10))
(6, 8, 10, 12, 14, 16, 18, 20, 22, 24)

How interesting. We had already started consuming our generator, reaching the number 4. Now the next 10 integers were generated on demand. There was no need for next calls or to build some kind of for loop or something like. islice takes care of that. And we can request the next 10 integers in the same way:

tuple(islice(p, 10))
(26, 28, 30, 32, 34, 36, 38, 40, 42, 44)

We now have perfect control over the consumption of this sequence.

The generator function itself could be passed as an argument, for example:

tuple(islice(positive_even_numbers(), 20))
(0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38)

The first 20 positive even integers were generated on the fly.

The famous Fibonacci sequence

Let’s be honest, the sequence of positive even numbers is not very practical. But the Fibonacci sequence is very famous and has its applications. Let’s use it and learn a little more about islice. First, let’s implement a generator for the Fibonacci sequence:

def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        b = a + b
        yield b
        a = a + b

Verifyin the beginning of the sequence:

f = fibonacci()
f
next(f)
0
next(f)
1
next(f)
1
next(f)
2

OK, it is working. We can again use islice as we did before, seeing the first 10 numbers of the sequence:

tuple(islice(fibonacci(), 10))
(0, 1, 1, 2, 3, 5, 8, 13, 21, 34)

We can also check how to use the second signature of the function: islice(iterable, start, stop[, step]).

For example, we can check which numbers are between positions 5 and 10:

tuple(islice(fibonacci(), 5, 10))
(5, 8, 13, 21, 34)

But be careful with the interpretation. The 5 refers to the index, and indexes in Python start at zero. So, see that in reality values were presented starting from the sixth in the sequence (compare with the result of the previous cell). And the last index is exclusive, similar to a slice of lists in Python.

The step parameter is also similar to that of lists:

tuple(islice(fibonacci(), 5, 10, 2))
(5, 13, 34)

If you want a specific position, just call a next at this position and pass None as stop:

next(islice(fibonacci(), 5, None))
5

Conclusion

One more step in understanding and using generators. This article is part of the “Python Drops” tag here on the site, with short articles on Python fundamentals. Until next time.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top