14Oct
artwork depicting the zip() function in Python
Zipping the information

Continuing our “Explaining Python Functions” series, we’re taking a closer look at the zip() function in Python. Its functionality may appear trivial, but it may very well surprise you with some of its quirky behaviors.

Here are some other Python articles that you might find interesting:

What Does zip() Do in Python?

Let’s begin with its syntax:

zip(iter1 [,iter2 [...]]) —> zip object

Python’s built-in help() module provides a brief — yet somewhat confusing — explanation:

Returns an iterator of tuples, where the i-th tuple contains the i-th element from each of the argument sequences or iterables. The iterator stops when the shortest input iterable is exhausted. With a single iterable argument, it returns an iterator of 1-tuples. With no arguments, it returns an empty iterator.

As always, this module proves useful when you’re well-versed in more general computer science and Python concepts; for a beginner programmer, however, this passage only raises more questions. Let’s try and explain the zip() function with examples, code snippets, and visualizations:

At its core, the zip() function allows you to:

  1. Take elements from a number of iterables and…
  2. put them together

We can demonstrate zip()’s functionality with a few lists:

uppercase = ['A', 'B', 'C']
lowercase = ['a', 'b', 'c']

for x, y in zip(uppercase, lowercase):
    print(x, y)

This will output:

A a
B b
C c

However, we aren’t limited to only two iterables that we pass as arguments — we can add as many as we’d like:

uppercase = ['A', 'B', 'C']
lowercase = ['a', 'b', 'c']
numbers = [1, 2, 3]

for x, y, z in zip(uppercase, lowercase, numbers):
    print(x, y, z)

This will output:

A a 1
B b 2
C c 3

Let’s visualize how the zip() function works in Python:

visualization of how the zip() function works in Python

Another important caveat of the zip() function has to do with iterables that contain unequal amounts of elements — those elements that wouldn’t get the pair become truncated:

uppercase = ['A', 'B', 'C', 'D', 'E']
lowercase = ['a', 'b', 'c', 'd']
numbers = [1, 2, 3]

for x, y, z in zip(uppercase, lowercase, numbers):
    print(x, y, z)

This will result in the following output:

A a 1
B b 2
C c 3

As we can see, there are only three triplets even though the listsuppercase and lowercase have five and four elements, respectively.

One important thing to know is what the zip() function returns. Although it may seem that we get a list upon calling this function, it actually returns a special data type called zip object. We’ learn how to convert it into other data types (e.g. list) in the sections below, but you do need to keep this little detail in mind.

The caveat associated with zip objects is that they aren’t subscriptable, meaning it’s impossible, among other things, to navigate through it using indices. The workaround involves converting the zip object into a list — we’ll learn how to do it in the next section.

You may have noticed that terms like iteration, iterable, and iterator appear frequently. These concepts are an integral part of computer science and general, so it’s important to drive their meaning home. We’ll reuse the explanation that we produced in the article about the map() function:

  • Iteration is a general computer science term; it refers to performing an action to a set of elements, one element at a time. A good example is the loop — it works with each individual item until the whole set of items is exhausted.
  • An Iterable is an object that we can loop over (e.g. a string, a list, or a file). From the Iterable, we get the iterator.
  • An iterator is an object that represents a stream of data; it returns the data one element at a time. It also remembers its position during iteration. Essentially, it governs how the iterable object should be iterated over.

artwork depicting a stylized Soshace banner

Converting a zip() Object into a List (and Using Indices)

We’ve established that the zip() function returns a zip object — this behavior is similar to how map() operates. The zip object offers some interesting functionality (for instance, it’s faster to iterate over it than the list), but we often need to convert it to list. In order to do this, let’s call the list() function:

b = ["red", "green", "blue"]
c = ["leopard", "cheetah", "jaguar"]

print(list(zip(b, c)))

This will output:

[('red', 'leopard'), ('green', 'cheetah'), ('blue', 'jaguar')]

As we can see, the list() function converted the zip object into a list of tuples. We can use indices to navigate through individual tuples. For readability’s sake, we’ll first assign our new list to a variable:

b = ["red", "green", "blue"]
c = ["leopard", "cheetah", "jaguar"]
new_list = list(zip(b, c))
print(new_list[0])
print(new_list[1])
print(new_list[2])

This will output:

('red', 'leopard')
('green', 'cheetah')
('blue', 'jaguar')

Converting a zip() Object into a Dictionary

Additionally, the dict() function can be used to transform the zip object into a dictionary. One thing to note is that you can only use two zip() arguments — the former will produce the keys, while the latter will produce the keys’ values:

b = ["red", "green", "blue"]
f = ["strawberry", "kiwi", "blueberry"]

print(dict(zip(b, f)))

This will output:

{'red': 'strawberry', 'green': 'kiwi', 'blue': 'blueberry'}

Unzipping a List

In some scenarios, we need to perform the opposite action — unzip an iterator. The unzipping operation involves returning the zipped elements to their original states. To do this, we add the * operator to our function call. Let’s take the following code as an example:

a = [1, 2, 3]
b = [4, 5, 6]
zipped = zip(a, b)
list(zipped)

a2, b2 = zip(*zip(a, b))
print(a == list(a2) and b == list(b2))

This will output:

True

Combining Zip() with List Comprehensions (And Potential Problems with for Loops)

visualization of how the zip() function works with a for loop in Python
Mind the missing elements after applying the for loop!

Another Python’s great feature — list comprehensions — can be used in conjunction with the zip() function. It seems pretty simple on the surface…

m = ["mind", "mouse", "mini"]
n = ["norm", "night", "necklace"]

[print(a, b) for a, b in zip(m, n)]

This will result in the following output:

mind norm
mouse night
mini necklace

Looks pretty easy and doesn’t seem to contain any bugs, right? Yes — for now. If we want to take the a argument from the list comprehension and print it, we get a NameError and that’s completely normal because a isn’t a real variable outside the list comprehension:

Traceback (most recent call last):
  File "C:\Pro\Py\tp-ex\tmp1.py", line 5, in 
    print(a)
NameError: name 'a' is not defined

However, if we decide to use a for lop instead of a list comprehension and then print a (or, in our case, m), we’ll get some weird results. Keep in mind that the for loop outputs the same result as list comprehension.

m = ["mind", "mouse", "mini"]
n = ["norm", "night", "necklace"]


for m, n in zip(m, n):
    print(m, n)

print(m)

The resulting output is…

mind norm
mouse night
mini necklace
mini

Wait, what’s that rebellious mini doing here alone? As it turns out, the m variable which previously referred to the "mind", "mouse", "mini" list got overwritten! Therefore, you should bear in mind that list comprehensions and for loops operate quite differently.

Conclusion

Well, turns out that the zip() function in Python does have a few tricks up its sleeve! 🙂 As always, we encourage you to actually play around with our code examples rather than only read the article. If you interact with code and tweak it, you’re sure to run into some unique problems — and solving them will help you remember this material far better.

Leave a Reply