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:
- Python map() Explained and Visualized
- Python enumerate() Explained and Visualized
- Python range() Explained and Visualized
- Responsible Web Scraping: Gathering Data Ethically and Legally
- “Learn Python the Hard Way”: a Detailed Book Review
- A Roundup Review of the Best Deep Learning Books
- Flask vs. Django: Let’s Choose the Right Framework for the Job
- Overview of Natural Language Processing Using Python Libraries
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:
- Take elements from a number of iterables and…
- … 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:
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.
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)
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.