September 11, 2024

Python Itertools

The itertools module in Python provides a collection of fast, memory-efficient tools that are useful for creating iterators for efficient looping. These tools are often used for working with combinatorial problems, generating sequences, and manipulating iterators. This module is part of Python’s standard library, so you don’t need to install anything extra to use it.

1. Importing the Itertools Module

You can start using the itertools module by importing it:

import itertools

2. Infinite Iterators

These iterators can produce values infinitely. Use caution when using them, as they can lead to infinite loops if not handled properly.

2.1. count()

The count() function returns an iterator that generates consecutive integers, starting from a specified number (default is 0) and increments by a specified step (default is 1).

import itertools

# Create an infinite iterator starting from 10
counter = itertools.count(start=10, step=2)

# Get the first five values
for _ in range(5):
    print(next(counter))

Output:

10
12
14
16
18

2.2. cycle()

The cycle() function returns an iterator that repeats the elements of an iterable indefinitely.

import itertools

# Create an infinite iterator that cycles through the list
cycler = itertools.cycle(['A', 'B', 'C'])

# Get the first six values
for _ in range(6):
    print(next(cycler))

Output:

A
B
C
A
B
C

2.3. repeat()

The repeat() function returns an iterator that repeatedly yields the same value a specified number of times. If no number is specified, it repeats indefinitely.

import itertools

# Create an iterator that repeats the value 5 three times
repeater = itertools.repeat(5, times=3)

# Get the repeated values
for value in repeater:
    print(value)

Output:

5
5
5

3. Combinatoric Iterators

These iterators are used to generate permutations, combinations, and Cartesian products of input iterables.

3.1. product()

The product() function returns the Cartesian product of input iterables. It is equivalent to nested loops in a generator expression.

import itertools

# Cartesian product of two lists
prod = itertools.product([1, 2], ['A', 'B'])

# Convert the product to a list and print
print(list(prod))

Output:

[(1, 'A'), (1, 'B'), (2, 'A'), (2, 'B')]

3.2. permutations()

The permutations() function returns successive r-length permutations of elements in the input iterable.

import itertools

# Permutations of length 2 from the list [1, 2, 3]
perms = itertools.permutations([1, 2, 3], 2)

# Convert to a list and print
print(list(perms))

Output:

[(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]

3.3. combinations()

The combinations() function returns r-length tuples of all possible combinations from the input iterable, without repeating elements.

import itertools

# Combinations of length 2 from the list [1, 2, 3]
combs = itertools.combinations([1, 2, 3], 2)

# Convert to a list and print
print(list(combs))

Output:

[(1, 2), (1, 3), (2, 3)]

3.4. combinations_with_replacement()

The combinations_with_replacement() function returns r-length tuples of all possible combinations from the input iterable, allowing repeated elements.

import itertools

# Combinations with replacement of length 2 from the list [1, 2, 3]
combs_wr = itertools.combinations_with_replacement([1, 2, 3], 2)

# Convert to a list and print
print(list(combs_wr))

Output:

[(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)]

4. Terminating Iterators

These iterators are used to process iterables in various ways, such as filtering, accumulating, and grouping elements.

4.1. accumulate()

The accumulate() function makes an iterator that returns accumulated sums (or the result of other binary functions, specified via the optional func argument).

import itertools

# Accumulate sums of the list [1, 2, 3, 4]
acc = itertools.accumulate([1, 2, 3, 4])

# Convert to a list and print
print(list(acc))

Output:

[1, 3, 6, 10]

4.2. chain()

The chain() function takes several iterables as input and returns a single iterator that yields elements from the first iterable until it is exhausted, then proceeds to the next iterable, until all of the iterables are exhausted.

import itertools

# Chain multiple iterables together
chained = itertools.chain([1, 2], ['A', 'B'], ['x', 'y'])

# Convert to a list and print
print(list(chained))

Output:

[1, 2, 'A', 'B', 'x', 'y']

4.3. filterfalse()

The filterfalse() function returns only those elements of an iterable for which the predicate is false.

import itertools

# Filter out even numbers (where predicate is true)
filtered = itertools.filterfalse(lambda x: x % 2 == 0, [1, 2, 3, 4, 5, 6])

# Convert to a list and print
print(list(filtered))

Output:

[1, 3, 5]

4.4. groupby()

The groupby() function returns consecutive keys and groups from the iterable. The key function is used to compute a value to group the iterable elements.

import itertools

# Group elements by their value mod 2
grouped = itertools.groupby([1, 2, 2, 3, 4, 4, 5], key=lambda x: x % 2)

# Print grouped elements
for key, group in grouped:
    print(key, list(group))

Output:

1 [1]
0 [2, 2]
1 [3]
0 [4, 4]
1 [5]

5. Practical Examples

The itertools module is incredibly useful for solving practical problems, such as

generating permutations, creating combinations, and chaining multiple iterables.

5.1. Example: Generating All Possible Passwords

Generate all possible 3-letter passwords using lowercase letters:

import itertools
import string

# Generate all possible 3-letter passwords
passwords = itertools.product(string.ascii_lowercase, repeat=3)

# Print the first 10 passwords
for _ in range(10):
    print(''.join(next(passwords)))

5.2. Example: Creating a Cartesian Product of Lists

Create a Cartesian product of two lists:

import itertools

# Cartesian product of two lists
cartesian_product = itertools.product([1, 2], ['A', 'B'])

# Convert to a list and print
print(list(cartesian_product))

Output:

[(1, 'A'), (1, 'B'), (2, 'A'), (2, 'B')]