Python Itertools Module.
Tags: python, tutorial, productivity, 100daysofcode
Published: 2022-04-05 13:00:48

Modules in python are very important and help a very great deal when it comes to programming. For more information about modules, [check here] (https://dev.to/joshwaoti/python-libraries-modules-and-packages-gp6). In this article, we'll focus on an exciting python module called Itertools.

First, let's define an iterable and an iterator.

Iterable is a function that has the __iter__() method on it.
Iterator is a function that has a __next__() method on it.


### 1. Infinite Iterators

They can return an infinite sequence by using a generator sequence. 
Infinite iterators have several functions, we'll look at a few.

#### i. count

This functions as the name suggests, counts through iterable items.

##### Example:

```python
from itertools import count

def count_example(start, step):
    counter = count(start, step)

    for c in counter:
        print(c)

        if c == 100:
            break

count_example(10, 5)

'''
The output:
10
15
20
25
30
35
40
45
50
55
60
65
70
75
80
85
90
95
100
'''
```
This is an infinite sequence, that is why we've had to break it at 100. otherwise it would have continued counting infinitely.

#### ii. repeat

The repeat function takes in an element, it could be a string, a list or any iterable element and the maximum number of time you want to repeat something, the maximum number argument is optional. However if you don't provide it the loop will run infinitely. 

##### Example:

```python
from itertools import repeat

def repeat_example(element, max_repeats):
    repeater = repeat(element, max_repeats)

    for val in repeater:
        print(val)

repeat_example('hello', 10)

'''
the output:
hello
hello
hello
hello
hello
hello
hello
hello
hello
hello
'''
```

It is clear that it repeats the hello function 10 times, because we input 10 as the maximum number.

#### iii. cycle

This one takes the iterable of object a string, list, tuple or any iterable object and cycles through them infinitely unless you provide the maximum number of times you want the cycle to go.
##### Example:
```python
from itertools import cycle

def cycle_example(elements):
    i = 0
    cycler = cycle(elements)
    while i < 100:
        print(next(cycler), end=" ")
        i += 1

cycle_example('ABCDEF')
'''
The output:
A B C D E F A B C D E F A B C D E F A B C D E F A B C D E F A B C D E F A B C D E F A B C D E F A B C D E F A B C D E F A B C D E F A B C D E F A B C D E F A B C D E F A B C D E F A B C D E F A B C D
'''
```

We called the (next()) method on the cycler iterator object manually or we could use a for loop.

### 2. Terminator Iterators

These one have a definite number of iterators that they will return to you. In simpler terms, they are not infinite.
Examples of functions that are terminator iterators:

#### i. accumulate

These function gives you the sum of elements of items at current position and prior in the list.

##### Example:

```python
from itertools import accumulate

def accumulate_example(elements):
    running_sum = accumulate(elements)
    print(list(running_sum))

accumulate_example([1,2,3,4,5,6,7,8,9,10])
'''
The output:
[1, 3, 6, 10, 15, 21, 28, 36, 45, 55]
'''
```
Using the accumulate method, we called the list function manually.

You can as well produce the same result by using a for loop, the advantage of itertools is that they don't store the values in memory, you can call the function to give you the values when you want to use them but it doesn't store the values in space therefore it preserves much memory.

#### ii. Chain

This function chains iterable objects together.

##### Example 1 :

```python
from itertools import chain

def chain_example(elements1, elements2):
    chained = chain(elements1, elements2)
    print(list(chained))

chain_example("123", "456")
'''
The output:
['1', '2', '3', '4', '5', '6']
'''
```

It is important to note that this method does not concatenate the items, but it it returns two iterators, it allows you to retrieve single items from the items.

We also have another method from the chain function called chain_from_iterable. Let's see an example of this:

##### Example 2:

```python
from itertools import chain

def chain_from_iterable_example(iterable):
    chained = chain.from_iterable(iterable)
    print(list(chained))

chain_from_iterable_example([[1,2,3], [4,5,6], [7,8,9], [10,11,12]])
'''
The output:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
'''
```
The method flattens our chained items and returns each single item in a single list.

#### iii. compress

This function takes in form of data source and selector and returns all the data if the corresponding items in the selector is true.

##### Example:

```python
from itertools import compress

def compress_example(data, selectors):
    compressed = compress(data, selectors)
    print(list(compressed))

compress_example([["a", "b", "c"], [1, 2, 3], [True, False, True]], [True, False, False])
'''
The output:
[['a', 'b', 'c']]
'''
```

True in this example means that we're keeping corresponding elements while false means we're not keep the correspoding elements.

#### iv. Pairwise

This one si going to pair all the adjacent items in our iterables.

##### Example:

```python
from itertools import pairwise

def pairwise_example(iterable):
    paired = pairwise(iterable)
    print(list(paired))

pairwise_example([1,2,3,4,5,6,7,8,9,10])
'''
The output:
[(1,2),(3,4),(5,6)(7,8),(9,10)]
'''
```

### 3. Combinatoric Iterators

#### i. Product

It returns the cartesian product of two iterable objects. it is kind of nested for loop.

##### Example:

```python
from itertools import product

def product_example(iterable1, iterable2):
    result = product(iterable1, iterable2)
    print(list(result))

product_example([1, 2, 3], ["a", "b", "c"])
'''
The output:
[(1, 'a'), (1, 'b'), (1, 'c'), (2, 'a'), (2, 'b'), (2, 'c'), (3, 'a'), (3, 'b'), (3, 'c')]
'''
```

#### ii. permutations

It returns all the permutations of a particular size of an iterable object.

##### Example:

```python
from itertools import permutations

def permutaions_example(iterable, size):
    result = permutations(iterable, size)
    print(list(result))

permutaions_example("ABCD", 2) 
'''
The output:
[('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'A'), ('B', 'C'), ('B', 'D'), ('C', 'A'), ('C', 'B'), ('C', 'D'), ('D', 'A'), ('D', 'B'), ('D', 'C')]
'''
```
#### iii. combinations

It returns all the combinations of a particular size of an iterable object.

```python
from itertools import combinations

def permutaions_example(iterable, size):
    result = combinations(iterable, size)
    print(list(result))

permutaions_example("ABCD", 2) 
'''
The output:
[('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'C'), ('B', 'D'), ('C', 'D')]
'''
```

The difference between combination and permutations is that in combinations the ordering does not matter. i.e. ('A', 'B') and ('B', 'A') are the same while in permutations they are not the same.

The fun part of the itertools is when you start mixing them out. You can combine infinite iterators with terminator or combinatoric iterators, or whichever order and with the desiring you want to have them appear.

There are numerous itertools functions mot mentioned in this article, feel free to check them out in the [itertools](https://docs.python.org/3/library/itertools.html) documentation and have fun😊 exploring the itertools module.