Back to Blog
Python

Python itertools pairwise: Syntax and Use Cases

python itertools pairwise: Learn how to use itertools.pairwise to iterate over successive pairs in Python, with syntax, examples, edge cases, and comparisons.

itertools
A visual representation of successive overlapping pairs from a sequence, showing two elements joined together.

python itertools pairwise requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

The itertools.pairwise function, introduced in Python 3.10, returns successive overlapping pairs from an input iterable. It is a simple but useful tool when you need to compare adjacent elements in a sequence. For an input like [1, 2, 3, 4], it yields (1, 2), (2, 3), and (3, 4) without creating any intermediate lists.

The Syntax and Return Type

itertools.pairwise takes a single iterable and returns an iterator. The iterator produces tuples, each containing two consecutive elements from the input. The function is a generator, so it evaluates the input lazily and does not load the entire sequence into memory at once.

from itertools import pairwise result = pairwise([1, 2, 3, 4]) print(list(result)) # [(1, 2), (2, 3), (3, 4)]

The returned object is an iterator, which means it can be consumed only once. If you need to reuse the pairs, convert them to a list first.

Basic Usage Examples

A common use is to compare each element with its predecessor. For instance, you might want to detect where a list changes value or where a difference exceeds a threshold.

from itertools import pairwise def has_duplicate_adjacent(items): return any(a == b for a, b in pairwise(items)) print(has_duplicate_adjacent([1, 2, 2, 3])) # True print(has_duplicate_adjacent([1, 2, 3, 4])) # False

Another typical pattern is computing the difference between consecutive measurements:

from itertools import pairwise readings = [10, 12, 9, 15] deltas = [b - a for a, b in pairwise(readings)] print(deltas) # [2, -3, 6]

Because pairwise yields tuples, you can unpack them directly in a loop or comprehension.

How pairwise Handles Short Iterables

If the input has fewer than two elements, pairwise yields nothing. This is consistent with the mathematical definition: you cannot form a pair from a single item.

from itertools import pairwise print(list(pairwise([]))) # [] print(list(pairwise([42]))) # []

This behavior is often convenient because it avoids special-case checks. If you need to handle the case where no pairs exist, you can simply check whether the output is empty, but in many algorithms an empty result is the correct outcome.

Memory and Lazy Evaluation

pairwise is a lazy iterator. It does not create a list of all pairs upfront. Instead, it reads one element at a time and keeps only the previous element in memory. This makes it suitable for large or infinite iterables, as long as you do not convert the entire result to a list.

from itertools import pairwise, islice # Works with an infinite sequence pairs = pairwise(range(10**9)) first_three = list(islice(pairs, 3)) print(first_three) # [(0, 1), (1, 2), (2, 3)]

The memory footprint is O(1) beyond the input iterator itself. This is a clear advantage over approaches that create a sliced copy of the input, such as zip(items, items[1:]), which duplicates the list.

Comparing pairwise with zip

Before Python 3.10, a common way to get successive pairs was to use zip with a sliced list:

items = [1, 2, 3, 4] pairs = zip(items, items[1:])

This works but has a hidden cost: items[1:] creates a new list containing all elements except the first. For large lists, that is an unnecessary allocation. pairwise avoids this by reading directly from the original iterator.

There is also a subtle difference when the input is an iterator rather than a list. With zip(items, items[1:]), the slicing operation fails because iterators do not support indexing. pairwise works with any iterable, including generators.

from itertools import pairwise def gen(): yield from [1, 2, 3, 4] print(list(pairwise(gen()))) # [(1, 2), (2, 3), (3, 4)]

If you need to pair elements with a step other than one, pairwise is not the right tool. For example, to compare every second element, you would combine islice with zip or use a custom generator.

Practical Use Cases

Beyond simple differences, pairwise is useful in several common programming tasks:

  • Detecting transitions in a sequence, such as finding where a boolean value flips.
  • Grouping consecutive equal elements by comparing adjacent pairs.
  • Computing moving averages or sliding-window calculations with a window size of two.
  • Validating that a sequence is strictly increasing or decreasing.

Here is an example that finds all indices where a value changes:

from itertools import pairwise def change_indices(items): return [i for i, (a, b) in enumerate(pairwise(items), 1) if a != b] print(change_indices([1, 1, 2, 2, 3])) # [2, 4]

The enumerate starts at 1 because the first pair corresponds to the boundary between index 0 and index 1.

Compatibility and Python Versions

itertools.pairwise was added in Python 3.10. If you are working in an environment that uses an older Python version, you can implement the same behavior with zip and itertools.islice:

from itertools import islice def pairwise_legacy(iterable): a, b = islice(iterable, None, None), islice(iterable, 1, None) return zip(a, b)

However, this implementation consumes two separate iterators, which may not work correctly if the input is a single iterator. A more robust version for older Python versions is:

def pairwise_legacy(iterable): it = iter(iterable) prev = next(it, None) for curr in it: yield prev, curr prev = curr

This version works with any iterable and preserves the lazy, memory-efficient behavior. If you control the Python version in your project, using the built-in pairwise is cleaner and less error-prone. For code that must run on Python 3.9 or earlier, the manual implementation is a straightforward substitute.

When upgrading to Python 3.10 or later, you can replace manual loops or zip-based patterns with pairwise to simplify the code and reduce the chance of off-by-one errors. The function is small but fits naturally into the itertools family of composable, lazy tools.

python itertools pairwise: Practical Usage and Code Examples | RYUSLOG DEV