itertools.pairwise() walks through an iterable and yields pairs made from consecutive elements. Instead of managing indexes manually, you receive (previous, current) directly, which makes code easier to read and reduces boundary mistakes.
The function was added in Python 3.10 and is useful for time-series analysis, sequence validation, transition detection, geometry, logs, event processing, and many other tasks. It is lazy, so pairs are produced only when requested and the whole input does not need to be copied into memory.
How pairwise works
from itertools import pairwise
values = [10, 14, 13, 20]
for previous, current in pairwise(values):
print(previous, current)
The output contains (10, 14), (14, 13), and (13, 20). An iterable with fewer than two items produces no pairs.
Calculate consecutive differences
temperatures = [21.2, 22.0, 21.7, 23.1]
differences = [b - a for a, b in pairwise(temperatures)]
This pattern appears in metrics, prices, sensor readings, and any data ordered over time. The code states the intention directly: compare each value with the next one.
Detect state transitions
states = ["offline", "offline", "online", "online", "error"]
for previous, current in pairwise(states):
if previous != current:
print(f"transition: {previous} -> {current}")
In observability systems, this approach identifies transitions without maintaining a separate variable for the previous state.
Validate increasing sequences
def strictly_increasing(values):
return all(a < b for a, b in pairwise(values))
Combining pairwise with all() keeps evaluation lazy and stops immediately after the first violation. That can save considerable work for large streams.
Find gaps between dates
from datetime import date
dates = [date(2026, 1, 3), date(2026, 1, 8), date(2026, 1, 10)]
gaps = [(b - a).days for a, b in pairwise(dates)]
The same idea applies to timestamps, software versions, geographic positions, workflow steps, and checkpoints.
Build line segments
When a list represents points in a route, every consecutive pair defines a segment.
points = [(0, 0), (3, 4), (6, 4)]
segments = list(pairwise(points))
This is clearer than range(len(points) - 1) and avoids repeated index access.
Pairwise versus zip and slicing
pairs = zip(values, values[1:])
For small lists, zip with slicing works. However, the slice creates another list and the pattern does not apply directly to generators. Pairwise accepts any iterable and preserves lazy behavior.
Pairwise versus tee
Before Python 3.10, a common recipe used itertools.tee(). It remains useful for compatibility, but pairwise communicates the intention more clearly.
from itertools import tee
def compatible_pairwise(iterable):
first, second = tee(iterable)
next(second, None)
return zip(first, second)
tee may buffer values internally when cloned iterators advance at different speeds. The standard pairwise implementation handles the required state for this specific case.
Use pairwise with generators
def readings():
for value in range(1_000_000):
yield value
for a, b in pairwise(readings()):
if b - a != 1:
break
No million-item list is created. This makes pairwise appropriate for files, network streams, database cursors, and long processing pipelines.
Detect missing identifiers
ids = [100, 101, 102, 106, 107]
for a, b in pairwise(ids):
if b != a + 1:
print("gap between", a, "and", b)
The pattern is useful for sequence numbers, batches, offsets, invoice ranges, and event logs.
Combine pairwise with enumerate
for index, (a, b) in enumerate(pairwise(values), start=1):
print(index, a, b)
With a starting value of one, the index corresponds to the position of the second element in the original sequence. Document that convention when it matters.
Circular sequences
Pairwise does not connect the last item back to the first. For a cycle, append or chain the first item explicitly.
from itertools import chain
colors = ["red", "green", "blue"]
circular_pairs = pairwise(chain(colors, colors[:1]))
For a generator, capture only the first item and chain it at the end, taking care to handle an empty iterable.
Do not mutate the input while iterating
Changing a list while pairwise is consuming it can produce surprising results. Prefer creating a new sequence or applying transformations before building the pairs.
Handle missing data deliberately
Real-world series often contain None, NaN, or invalid records. Decide whether to filter, stop the segment, or keep the invalid pair for reporting.
data = [10, None, 12, 15]
valid = (x for x in data if x is not None)
print(list(pairwise(valid)))
Filtering changes adjacency: 10 and 12 become neighbors. That may be correct in one domain and misleading in another, so the policy should be explicit.
Performance characteristics
Pairwise has linear time complexity and uses little extra memory. Because it does not materialize every pair, it is normally efficient. The function applied to each pair may still dominate total execution time, especially if it performs I/O or expensive calculations.
Common mistakes
- Expecting output from an iterable with one item.
- Assuming the final item is connected to the first.
- Consuming a generator before passing it to pairwise.
- Ignoring missing values that alter adjacency semantics.
- Using pairwise when the task needs windows of three or more items.
Larger sliding windows
Pairwise creates windows of exactly two elements. For larger sliding windows, use a deque-based recipe or aligned iterators. itertools.batched creates non-overlapping groups, which is a different operation and should not be confused with a sliding window.
Testing pairwise logic
Test empty input, one item, two items, repeated values, invalid values, and very large generators. Property-based tests are especially helpful for monotonicity and gap detection because they can generate many sequence shapes automatically.
Best practices
Confirm that data is sorted in the intended order, preserve laziness when possible, and use descriptive names such as previous and current. Continue learning with Academify guides on Python itertools, generators, list comprehensions, and Python functions.
Conclusion
itertools.pairwise is a small tool that solves a recurring problem elegantly. It removes manual indexing, accepts any iterable, and combines naturally with all, comprehensions, and lazy pipelines. Whenever you need to compare neighbors, detect transitions, or calculate deltas, pairwise should be one of the first options you consider.







