Mastering the Itertools Module in Python
All the tricks right on your tips
If you have been doing python, you must have definitely come across the itertools module. It might not look like it, but I can tell you that it is one of the most powerful libraries on python. If you wish to write a more efficient, clean, and more ‘pythonic’ code, I believe that itertools is a must-have tool to add to your arsenal. Here in this article we will introduce some functionalities of the library and delve into some examples to help us better understand it.
However, the thing about itertools is that it is not enough to just know the definitions of the functions it contains. The real power lies in composing these functions to create fast, memory-efficient, and good-looking code. So here we will look at definitions along with some practical examples of the functions to understand their applications.
For an overview let us look at what the module’s official documentation has to say:
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
Itertools provides us with three different types of iterators
- Infinite iterators
- Terminating iterators
- Combinatoric iterators
Let’s now dive into it
Python iterators like lists, tuples, dictionaries are exhaustive. But it is not necessary for an iterator to exhaust at some point, they can go on forever. Let’s look at the three types of infinite iterators.
- count(start = 0, step = 1): This iterator starts returning values from ‘start’ and goes on infinitely, if steps are provided then those values are skipped. Let’s look at examples to see how it works.
We can also pass negative and floating arguments to this function.
- cycle(iterable): It returns all values from the passed argument. When the iterable object is exhausted it starts printing from the beginning again.
Now use the next function to print the values.
- repeat(object[, times]): This makes an iterator that returns the object over and over again. Runs indefinitely unless the time’s argument is specified.
A common use of this iterator is to supply a stream of values to map() or zip().
That was all about infinite iterators. From the above examples, it is clear that there may e situations where these can come in handy. Now let’s look at the real deal of the itertools library. The terminating iterators.
These iterators are used to work on finite sequences and produce an output based on the function used.
- accumulate(iterable[, func, *, initial=None]): This makes an iterator that returns accumulated results of binary functions (specified via the optional
funcis supplied, it should be a function of two arguments. Elements of the input iterable may be any type that can be accepted as arguments to
func. It will become more clear with some examples.
Let’s pass the optional
It is very similar
functools.reduce()which returns only the last accumulated value, but that is a story for another day!
- chain(*iterables): This makes an iterator that returns elements from the first iterable until it is exhausted, then proceeds to the next iterable, until all of the iterables are exhausted. In other words, it makes a single ‘sequence’ from a sequence of sequences.It has a function
chain.from_iterabe()which works in a similar fashion, but it takes an argument as a list of lists instead of multiple lists.
- compress(data, selectors): This makes an iterator that filters elements from data returning only those that have a corresponding element in selectors that evaluates to True.It stops when either of the two is exhausted.
- dropwhile(predicate, iterable): This iterator starts returning the characters only after the
predicatein argument returns false for the first time.
- filterfalse(predicate, iterable): This makes an iterator that filters elements from iterable returning only those for which the predicate is
False. If predicate is
None, it returns the items that are false.
- groupby(iterable, key=None): This makes an iterator that returns consecutive keys and groups from the iterable. The key is a function computing a key value for each element. If not specified or is
None, key defaults to an identity function and returns the element unchanged.
- islice(iterable, start, stop[, step]): This iterator selectively returns the values from iterable container passed as the argument. Unlike regular slicing, it does not support negative values.
- starmap(function, iterable): This iterator takes a function and a list of tuples as an argument and returns the value according to the function from each tuple in the list.
- takewhile(predicate, iterable): This makes an iterator that returns elements from the iterable as long as the predicate is true. It is the opposite of
- tee(iterable, n=2): This simple returns n independent iterators from a single iterable. If n is not passed, the default value of n is 2.
- zip_longest(*iterables, fillvalue=None): This Make an iterator that aggregates elements from each of the iterables. If the iterables are of uneven length, missing values are filled-in with
fillvalue. Iteration continues until the longest iterable is exhausted.
These were all the terminating iterators provided by the module. You can clearly see the variety of functionalities that are provided by these iterators. These can be used in your code to make it shorter, more efficient, and also more “pythonic”.
I hope that you found these explanations easy to understand and the examples gave you the hang of it all. As you will go along, you will find more beautiful applications of these functions in solving your problems.
This wraps this article and I’ll come back with the second part of Mastering itertools where we will see the combinatoric iterators along with some more complex examples of all the functions. If you have any suggestions, feedback, or simply want to connect, I’ll be delighted to reach out. You can connect with me on LinkedIn or find some of my work on Github.