How Python zip Handles Unequal Length Iterables
This article explains how Python's built-in zip()
function behaves when supplied with iterables of different lengths. By
default, zip() stops pairing elements as soon as the
shortest input is exhausted, silently ignoring any remaining items. You
will learn about this default truncation behavior, how to prevent silent
bugs using the strict=True parameter introduced in Python
3.10, and how to preserve all values using the standard library's
itertools.zip_longest() function.
Default Behavior: Silent Truncation
By default, zip() pairs elements from each iterable
sequentially based on their index. When the provided iterables have
unequal lengths, zip() terminates immediately when the
shortest iterable runs out of elements. Any remaining elements in the
longer iterables are omitted without warning.
letters = ["a", "b", "c", "d"]
numbers = [1, 2]
result = list(zip(letters, numbers))
print(result)
# Output: [('a', 1), ('b', 2)]In the example above, 'c' and 'd' are
discarded. Because this occurs without an error or warning, it can lead
to silent bugs when the data sources are expected to be of identical
size.
Enforcing Equal Lengths:
strict=True
In Python 3.10 and later, zip() includes an optional
boolean parameter called strict. When set to
strict=True, zip() verifies that all iterables
are of equal length. If one iterable ends before another, the function
raises a ValueError.
letters = ["a", "b", "c", "d"]
numbers = [1, 2]
try:
result = list(zip(letters, numbers, strict=True))
except ValueError as e:
print(f"Error: {e}")
# Output: Error: zip() argument 2 is shorter than argument 1Using strict=True is best practice whenever input
collections are required to correspond one-to-one, as it immediately
surfaces mismatched data lengths.
Keeping All Elements:
itertools.zip_longest()
If the goal is to process every element regardless of iterable
length, use zip_longest() from the built-in
itertools module. Instead of stopping at the shortest
iterable, it iterates until the longest iterable is exhausted, filling
in missing slots with a placeholder.
from itertools import zip_longest
letters = ["a", "b", "c", "d"]
numbers = [1, 2]
# Missing values default to None
result = list(zip_longest(letters, numbers))
print(result)
# Output: [('a', 1), ('b', 2), ('c', None), ('d', None)]
# Custom placeholder using fillvalue
result_custom = list(zip_longest(letters, numbers, fillvalue=0))
print(result_custom)
# Output: [('a', 1), ('b', 2), ('c', 0), ('d', 0)]Summary of Options
zip(*iterables): Truncates output to the shortest iterable. Best when unaligned trailing data should be ignored.zip(*iterables, strict=True): Raises aValueErrorif lengths do not match. Best for strict validation in Python 3.10+.itertools.zip_longest(*iterables, fillvalue=...): Continues until the longest iterable is finished, padding missing items. Best when no data can be discarded.