Pairwise Iteration
`itertools.pairwise` (Python 3.10+) yields successive overlapping pairs from any iterable. It replaces the classic `zip(seq, seq[1:])` and the `tee` recipe with a single, lazy, memory-flat call. This entry covers the basic pattern, the manual fallback for older Python, and a tiny example: detecting monotonic runs.
1,059 views
27
from itertools import pairwise
nums = [10, 20, 25, 40, 41, 100]
pairs = list(pairwise(nums))
print(pairs)
# [(10, 20), (20, 25), (25, 40), (40, 41), (41, 100)]
# Differences are a one-line follow-up.
deltas = [b - a for a, b in pairwise(nums)]
print(deltas)
# [10, 5, 15, 1, 59]pairwise(iterable) yields (s0, s1), (s1, s2), (s2, s3), ... until the source has fewer than two items left. It works on any iterable (list, tuple, generator, file object), not just sequences, and it does not rebuild internal state between iterations. Use it whenever a calculation reads two adjacent items at a time: deltas, ratios, monotonic checks, edge construction from a path. It costs O(n) time and O(1) extra memory regardless of input size.
from itertools import tee
def pairwise(iterable):
"""Yields (s0, s1), (s1, s2), (s2, s3), ... like itertools.pairwise."""
a, b = tee(iterable)
next(b, None) # drop the first item from b so it leads by 1
return zip(a, b)
print(list(pairwise([1, 2, 3, 4])))
# [(1, 2), (2, 3), (3, 4)]
# tee buffers items as the slower iterator falls behind, but for pairwise
# the lag is at most one item, so the buffer never grows.Before 3.10, the standard recipe was a 4-line wrapper around itertools.tee. tee(iterable) returns two independent iterators sharing the same source, then advancing one by next(b, None) makes it lead by exactly one item. zip then pairs them up and stops at the shorter iterator (the trailing one). It is worth knowing this recipe both for older codebases and because the same tee + offset + zip shape generalizes to triple-wise or N-wise sliding windows.
from itertools import pairwise
stock = [100, 102, 105, 105, 103, 110, 115, 120]
def longest_increasing_run(values):
if len(values) < 2:
return len(values)
best = run = 1
for prev, curr in pairwise(values):
if curr > prev:
run += 1
best = max(best, run)
else:
run = 1
return best
print(longest_increasing_run(stock)) # 4 (100, 102, 105 plus the start of the run)
# 'is the whole list non-decreasing?'
print(all(a <= b for a, b in pairwise([1, 1, 2, 3]))) # True
print(all(a <= b for a, b in pairwise([1, 2, 1, 3]))) # FalsePairwise turns 'compare each item to its predecessor' into a flat loop with no index bookkeeping. Tracking the best monotonic streak becomes a five-line function: bump run when the relation holds, reset it otherwise. The all(a <= b for a, b in pairwise(seq)) idiom is the cleanest way to ask 'is this sorted?' without sorting it first. The same trick generalizes to 'are all consecutive deltas equal?' for arithmetic-progression detection.
