Did you know you can create an infinite sequence without running into memory
problems and still consume that sequence however you like? As your
understanding of the Python language grows, you start exploring the modules in
the Python standard library. Today we will look at a specific function from the
itertools module, islice, which is excellent for our purpose.
Getting to know the function #
Whenever you hear about some feature of your favorite programming language (or
the language that pays your bills…) get to know its 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 about. Then come back to this article :-).
As for the islice function, the
documentation
describes it as:
itertools.islice(iterable, stop)oritertools.islice(iterable, start, stop[, step]). Make an iterator that returns selected elements from the iterable. (…)
A very concise description. Let’s start by importing the function:
from itertools import isliceCreating our infinite sequence #
Now let’s create our infinite sequence. If you have already read the article about generators you know that creating such sequences is perfectly possible. Just create a generator and produce the values on demand:
def positive_evens():
value = 0
while True:
yield value
value += 2Note that the generator will produce non-negative even integers starting from zero whenever requested. Let’s quickly recall the behavior of a generator like this. First, seeing that Python recognizes it as a generator:
positive_evens()<generator object positive_evens at 0x7fac9d1deeb0>
Let’s bind it to a variable so we can consume it through next calls:
p = positive_evens()
next(p)0
next(p)2
next(p)4
And we could go on indefinitely. Now let’s understand what islice does.
Consuming the sequence in a controlled way #
Let’s take a more detailed look at the description of the function:
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.
Note that there are two signatures. The first is islice(iterable, stop). So
let’s pass our iterable (the generator) and a stopping value, for example, 10:
islice(p, 10)<itertools.islice at 0x7fac9c16ee50>
Note that it really is an iterator. Let’s then consume it by 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 for building a for loop or anything like that. islice
takes care of it. And we can request the next 10 integers 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_evens(), 20))(0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38)
The first 20 non-negative even integers were generated directly.
The famous Fibonacci sequence #
Let’s face it: the sequence of positive even numbers is not very practical. But
the Fibonacci sequence is
quite famous and has its applications. Let’s use it and learn a bit 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 + bLet’s look at the beginning of the sequence to check our implementation:
f = fibonacci()
f<generator object fibonacci at 0x7fac9c16a6d0>
next(f)0
next(f)1
next(f)1
next(f)2
OK, it works. We can, again, use islice as we did before, looking at the
first 10 numbers of the sequence:
tuple(islice(fibonacci(), 10))(0, 1, 1, 2, 3, 5, 8, 13, 21, 34)
We can also see how to use the second signature of the function:
islice(iterable, start, stop[, step]).
For example, we can see which numbers lie 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 indices in Python start at zero. So, note that the values shown actually start 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 Python lists.
The last parameter is the step, also similar to that of lists:
tuple(islice(fibonacci(), 5, 10, 2))(5, 13, 34)
If you want one specific position, just call next on it and pass None as
stop:
next(islice(fibonacci(), 5, None))5
Conclusion #
One more step in understanding and using generators. I hope the articles are connecting well with each other and adding to your knowledge.
Did you like this article? It is part of the programming Drops, a set of shorter posts focused on fundamentals, covering some aspects of the Python language and of programming in general. You can read more of these articles by browsing the tag “drops” here on the site.
See you next time!