Thanks to visit codestin.com
Credit goes to www.pythonmorsels.com

zip with different length iterables PREMIUM

Series: Looping
Trey Hunner smiling in a t-shirt against a yellow wall
Trey Hunner
4 min. read 3 min. video Python 3.10—3.14
Python Morsels
Watch as video
03:13

What happens if you use Python's built-in zip function with iterables that have different lengths?

Zipping iterables matches up each iterable item that's at the same position

Python's zip function is helpful for looping over multiple iterables at the same time:

>>> numbers = [2, 1, 3, 4, 7]
>>> letters = "abcde"
>>> for number, letter in zip(numbers, letters):
...     print(f"{number} is {letter}")
...
2 is a
1 is b
3 is c
4 is d
7 is e

The zip function isn't related to .zip files. It's related to a zipper on a piece of clothing.

Similar to a zipper, zipping two iterables together will match up each of the iterable items that are at the same position:

>>> list(zip(numbers, letters))
[(2, 'a'), (1, 'b'), (3, 'c'), (4, 'd'), (7, 'e')]

Though, unlike with a physical zipper, the zip function also works with any number of iterables. Here we're zipping together three different iterables:

>>> numbers = [2, 1, 3, 4, 7]
>>> letters = "abcde"
>>> names = ["Luciano", "Jane", "Sandra", "Iva", "Diana"]
>>> list(zip(numbers, letters, names))
[(2, 'a', 'Luciano'), (1, 'b', 'Jane'), (3, 'c', 'Sandra'), (4, 'd', 'Iva'), (7, 'e'
, 'Diana')]

The zip function works with any iterables

Python's zip function works with any iterable, not just sequences. For example, we could zip together a list and a generator object:

>>> numbers = [2, 1, 3, 4, 7]
>>> squares = (n**2 for n in numbers)
>>> list(zip(numbers, squares))
[(2, 4), (1, 1), (3, 9), (4, 16), (7, 49)]

Or we could zip together each of the lines in two different files:

>>> with open("file1.txt") as file1, open("file2.txt") as file2:
...     for line1, line2 in zip(file1, file2):
...         print(line1.strip(), line2.strip())
...
The light of a candle In the twilight rain
is transferred to another candle- These brilliant-hued hibiscus -
spring twilight. A lovely sunset.

But what if the iterables don't have the same number of items?

What happens with different length iterables?

If we zip together iterables with different numbers of items, what might Python do?

>>> lucas = [2, 1, 3, 4, 7, 11, 18]
>>> fib = [1, 1, 2, 3, 5]
>>> list(zip(lucas, fib))

The zip function could raise an exception in this case. Or it could stop at the shortest iterable. Or it might go to the longest iterable.

It turns out the zip function stops at the shortest iterable:

>>> list(zip(lucas, fib))
[(2, 1), (1, 1), (3, 2), (4, 3), (7, 5)]

Why?

Well, this was the simplest thing for the Python core developers to implement.

The zip function works by getting an iterator from each of the given iterables, and then repeatedly calling next on each of these iterators. Once a StopIteration exception is raised by any of the iterators, zip stops:

def zip(*iterables):
    iterators = [iter(it) for it in iterables]
    while True:
        try:
            yield tuple([next(it) for it in iterators])
        except StopIteration:
            return

So the built-in zip function stops at the shortest iterable.

Continuing until the longest iterable

What if we have different length iterables, and we wanted Python to continue until the longest iterable ends?

The zip_longest function from Python's itertools module can do just this:

>>> lucas = [2, 1, 3, 4, 7, 11, 18]
>>> fib = [1, 1, 2, 3, 5]
>>> from itertools import zip_longest
>>> list(zip_longest(lucas, fib))
[(2, 1), (1, 1), (3, 2), (4, 3), (7, 5), (11, None), (18, None)]

The zip_longest function will loop until the longest iterable, filling in missing values with None by default.

But we can customize the fill value that it uses with an optional fillvalue argument:

>>> list(zip_longest(lucas, fib, fillvalue=0))
[(2, 1), (1, 1), (3, 2), (4, 3), (7, 5), (11, 0), (18, 0)]

Raising an exception when iterables have different lengths

Sometimes zipping iterables that have different lengths might indicate a bug in our code.

What if we wanted to raise an exception if we found ourselves zipping iterables that have different lengths?

As of Python 3.10, the zip function accepts an optional strict argument that defaults to False. When strict is set to True (or a truthy value) it will raise a ValueError exception if the two iterables have different numbers of items.

>>> lucas = [2, 1, 3, 4, 7, 11, 18]
>>> fib = [1, 1, 2, 3, 5]
>>> list(zip(lucas, fib, strict=True))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: zip() argument 2 is shorter than argument 1

It even works for iterables that don't have a length, like generator objects:

>>> letters = "abc"
>>> squares = (n**2 for n in range(10))
>>> list(zip(letters, squares, strict=True))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: zip() argument 2 is longer than argument 1

The strict argument doesn't actually check the length of the given iterables. It just raises an exception as soon as it discovers that one of the given iterables had more items than the other ones:

>>> letters = "abc"
>>> squares = (n**2 for n in range(10))
>>> for letter, n in zip(letters, squares, strict=True):
...     print(letter, n)
...
a 0
b 1
c 4
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: zip() argument 2 is longer than argument 1

Choose how zip should work with different length iterables

You can use the built-in zip function to loop over multiple iterables at the same time.

If you'd like to make sure your iterables have the same number of items, you can use the strict argument. If, instead, you'd like to loop until the longest iterable, you can use the zip_longest function from Python's itertools module.

Now it's your turn! 🚀

We don't learn by reading or watching. We learn by doing. That means writing Python code.

Practice this topic by working on these related Python exercises.

Series: Looping

Unlike, JavaScript, C, Java, and many other programming languages we don't have traditional C-style for loops. Our for loops in Python don't have indexes.

This small distinction makes for some big differences in the way we loop in Python.

To track your progress on this Python Morsels topic trail, sign in or sign up.

0%
Python Morsels
Watch as video
03:13
This is a free preview of a premium screencast. You have 2 previews remaining.