When you need to iterate over data efficiently in Python, the itertools module is the most powerful tool in the standard library. It provides functions that create iterators for efficient looping, blending functional programming concepts with Python's elegance.

In this complete guide, you will learn everything from the simplest iterators to advanced combinations, with practical examples that will transform how you write loops and process data in Python.

What Is the itertools Module?

The itertools module is part of Python's standard library and implements a collection of iterator-building functions inspired by constructs from functional programming languages like Haskell and APL. The official documentation describes it as a "toolkit for building and combining iterators."

The core philosophy behind itertools is to provide building blocks that can be composed together to solve complex iteration problems elegantly and efficiently. Every function in itertools returns an iterator (using lazy evaluation), which means they consume minimal memory even when handling large datasets.

Source: Official Python Documentation - itertools

Importing the Module

itertools comes built-in with Python, so you only need to import it:

import itertools

# Or import specific functions
from itertools import chain, cycle, count, product

Infinite Iterators

Infinite iterators generate values continuously with no defined limit. They are useful for simulating infinite sequences, counters, and repetitions.

count(start=0, step=1)

Generates consecutive numbers from a starting value with a defined step. Excellent for creating counters or automatic indices:

from itertools import count

for i in count(10, 2):
    if i > 20:
        break
    print(i, end=" ")  # 10 12 14 16 18 20

cycle(iterable)

Repeats an iterable infinitely. Commonly used to alternate through a fixed set of values:

from itertools import cycle

colors = cycle(['red', 'green', 'blue'])
for i in range(6):
    print(next(colors), end=" ")  # red green blue red green blue

repeat(object, times=None)

Repeats an object a specific number of times. Without the times parameter, it repeats indefinitely:

from itertools import repeat

for value in repeat(42, 5):
    print(value, end=" ")  # 42 42 42 42 42

Source: Real Python - Infinite Iterators in itertools

Combinatoric Iterators

These iterators generate every possible combination or permutation from a set of elements. They are essential for mathematics, statistics, and optimization problems.

product(*iterables, repeat=1)

Computes the Cartesian product of two or more iterables. It is equivalent to nested loops:

from itertools import product

for item in product([1, 2], ['a', 'b']):
    print(item, end=" ")  # (1, 'a') (1, 'b') (2, 'a') (2, 'b')

for item in product([1, 2], repeat=2):
    print(item, end=" ")  # (1, 1) (1, 2) (2, 1) (2, 2)

permutations(iterable, r=None)

Generates every possible permutation of r elements. The order of elements matters:

from itertools import permutations

for item in permutations([1, 2, 3], 2):
    print(item, end=" ")  # (1, 2) (1, 3) (2, 1) (2, 3) (3, 1) (3, 2)

combinations(iterable, r)

Generates every combination of r elements. Unlike permutations, the order of elements does NOT matter:

from itertools import combinations

for item in combinations([1, 2, 3], 2):
    print(item, end=" ")  # (1, 2) (1, 3) (2, 3)

combinations_with_replacement(iterable, r)

Similar to combinations, but allows elements to repeat within the same combination:

from itertools import combinations_with_replacement

for item in combinations_with_replacement([1, 2, 3], 2):
    print(item, end=" ")  # (1, 1) (1, 2) (1, 3) (2, 2) (2, 3) (3, 3)

Source: GeeksforGeeks - Python itertools Module

Source: Programiz - Python itertools Module

Terminating Iterators

These iterators consume one or more input iterables and produce a transformed result.

chain(*iterables)

Concatenates multiple iterables into a single sequence. Perfect for traversing several collections as if they were one:

from itertools import chain

result = list(chain([1, 2], [3, 4], [5, 6]))
print(result)  # [1, 2, 3, 4, 5, 6]

accumulate(iterable, func=operator.add)

Returns successively accumulated values. By default it uses addition, but it accepts any binary function:

from itertools import accumulate
import operator

print(list(accumulate([1, 2, 3, 4, 5])))  # [1, 3, 6, 10, 15]
print(list(accumulate([1, 2, 3, 4, 5], operator.mul)))  # [1, 2, 6, 24, 120]

groupby(iterable, key=None)

Groups consecutive elements based on a key function. The data must be sorted by the grouping key beforehand:

from itertools import groupby

data = [('alice', 'A'), ('bob', 'A'), ('maria', 'B'), ('pedro', 'B')]
data.sort(key=lambda x: x[1])

for letter, group in groupby(data, key=lambda x: x[1]):
    print(f"{letter}: {[name for name, _ in group]}")

islice(iterable, start, stop, step=1)

Slices an iterator just like you would slice a list, but without creating intermediate lists:

from itertools import islice

result = list(islice(range(100), 2, 8, 2))
print(result)  # [2, 4, 6]

compress(data, selectors)

Filters elements from data based on the boolean values in selectors:

from itertools import compress

data = ['A', 'B', 'C', 'D', 'E']
selectors = [1, 0, 1, 0, 1]
print(list(compress(data, selectors)))  # ['A', 'C', 'E']

dropwhile and takewhile

dropwhile discards elements while the condition is True and then yields the rest. takewhile does the opposite:

from itertools import dropwhile, takewhile

numbers = [1, 2, 3, 4, 5, 1, 2, 3]

print(list(dropwhile(lambda x: x < 3, numbers)))  # [3, 4, 5, 1, 2, 3]
print(list(takewhile(lambda x: x < 3, numbers)))  # [1, 2]

pairwise (Python 3.10+)

Returns overlapping tuples of consecutive elements. Great for comparing adjacent items:

from itertools import pairwise

print(list(pairwise([1, 2, 3, 4])))  # [(1, 2), (2, 3), (3, 4)]

starmap(func, iterable)

Similar to map, but unpacks each element as individual arguments:

from itertools import starmap

pairs = [(2, 3), (4, 5), (6, 7)]
print(list(starmap(lambda x, y: x * y, pairs)))  # [6, 20, 42]

zip_longest(*iterables, fillvalue=None)

Similar to zip, but continues until the longest iterable is exhausted, filling in missing values:

from itertools import zip_longest

a = [1, 2, 3]
b = ['a', 'b']
print(list(zip_longest(a, b, fillvalue='-')))  # [(1, 'a'), (2, 'b'), (3, '-')]

Source: Python Functional Programming HOWTO - itertools

Real-World Use Cases

1. Log Aggregation with groupby

from itertools import groupby

logs = [
    ('2026-05-01', 'ERROR', 'Connection failed'),
    ('2026-05-01', 'INFO', 'Server started'),
    ('2026-05-02', 'ERROR', 'Timeout exceeded'),
    ('2026-05-02', 'INFO', 'Backup completed'),
]

logs.sort(key=lambda x: x[1])
for level, group in groupby(logs, key=lambda x: x[1]):
    print(f"{level}: {len(list(group))} occurrences")

2. Password Generation with product

from itertools import product
import string

chars = string.ascii_lowercase + string.digits
passwords = product(chars, repeat=4)
# Useful for generating test combinations (use responsibly!)

3. Financial Analysis with accumulate

from itertools import accumulate

prices = [100, 102, 105, 103, 107, 110]
variation = [prices[i] - prices[i-1] for i in range(1, len(prices))]
print(list(accumulate(variation)))

4. Efficient File Reading with islice

from itertools import islice

def read_specific_lines(filepath, start, end):
    with open(filepath, 'r') as f:
        return list(islice(f, start, end))

Performance and Efficiency

One of the biggest advantages of itertools is memory efficiency. Since every function returns an iterator using lazy evaluation, you can process millions of elements without consuming memory proportionally.

import sys
from itertools import count

counter = count()
print(sys.getsizeof(counter))  # 48 bytes (approx.)

itertools functions are implemented in C, making them significantly faster than equivalent pure Python implementations. To better understand how iterators and generators work internally, check out our complete guide on Python Generators. And to master the compact expressions that complement itertools, see our tutorial on List Comprehensions in Python.

Source: Stack Overflow - Why is itertools important?

Combining itertools with Generators

itertools works seamlessly with generators. You can combine both techniques to create highly efficient data processing pipelines:

from itertools import islice, count

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

fib_gen = fibonacci()
first_10 = list(islice(fib_gen, 10))
print(first_10)  # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

Tips and Best Practices

  • Import specific functions - Instead of importing the whole module, import only the functions you need to keep your code cleaner.
  • Prefer itertools over manual loops - itertools functions are implemented in C and are significantly faster than equivalent Python loops.
  • Preserve lazy evaluation - Converting an iterator to a list prematurely defeats the memory benefit. Keep data as iterators until you actually need them.
  • Combine itertools with functools and operator - Use operator.add, operator.mul, etc. with accumulate and starmap for more expressive code.
  • Consider more-itertools - The third-party package more-itertools extends itertools with additional functions like chunked, windowed, and padded.

Source: DataCamp - Python itertools Tutorial

Conclusion

Python's itertools module is an indispensable tool for any developer working with data. Its functions for efficient iterator manipulation allow you to write cleaner, faster code with lower memory consumption.

Mastering itertools will significantly elevate the quality of your Python code, especially when combined with generators and list comprehensions. The concepts of lazy evaluation and iterator composition are fundamental to functional programming in Python.

Keep exploring the free guides on Universo Python to deepen your knowledge and become a more complete and efficient Python developer!