Python's zip() function helps developers group data from several data structures. The elements are joined in the order they appear; with it, you can iterate simultaneously over different data structures. All without using a single nested loop!
If this is what you’re trying to achieve, you will need to learn to use zip(). And the best thing about the function is that it’s not complicated to use.
In this guide, we will walk you through how to use zip() and iterate over different data structures parallelly.
What is zip() in Python?
In Python, zip() simultaneously merges several objects called iterables. It does this by creating a new object, the elements of which are all tuples.
The elements of the iterables are held in these tuples. Let’s look at a straightforward example to understand the zip() function.
Let’s say that we have two tuples with names in them, like so:
names1 = ("Jack", "Theo", "Jose") names2 = ("Duncan", "Chris", "Anthony")
So, if you want to pair the elements of these tuples, you can use zip(). The function loops like so:
print(zip(names1,names2))
Of course, the print function will help us see what happens when you use zip. Running this code, you’ll see it returning a particular zip object. The output of the code will look something like this:
<zip object at 0x7f7991fc4cc0>
Now, let’s get into how you can transform this zip object into a list. It’s as simple as writing:
print(list(zip(names1,names2)))
Running the code again, you will see the output:
[('Jack', 'Duncan'), ('Theo', 'Chris'), ('Jose', 'Anthony')] |
So far, we’ve covered the basics of how this function works. We will cover several specifics of the zip function in the coming sections.
If you want to take a look at the official definition of the zip() function, here’s the link to the official Python documentation page. Note that you will also find the source code of the zip() function in the documentation, which is helpful if you want to learn the technical workings of the function.
Using the zip() Function with Various Data Structures
In the example program in the previous section, we explored how you can use the zip() function to combine two lists into one object – the zip object. The elements of the object are tuples of length 2.
To harness the capabilities of the zip() function, you must understand that the function can work with all kinds of data structures. In this section, we’ll explore how zip() does this.
Using zip() with Tuples
Passing two tuples to the zip() function is similar to how you pass lists to it. So, the code would look like this:
names1 = ("Jack", "Theo", "Jose") names2 = ("Duncan", "Chris", "Anthony") print(list(zip(names1,names2)))
Running the code, the output would be:
[('Jack', 'Duncan'), ('Theo', 'Chris'), ('Jose', 'Anthony')] |
It should be clear that there is virtually no difference between using the zip() function with lists and tuples. Convenient!
Using zip() with Dictionaries
While you can use zip() with dictionaries, there are some limitations that you need to be aware of.
Let’s get into the nitty-gritty with an example:
names1 = {"mom":"Sonia", "dad":"Jesse"} names2 = {"firstChild":"Andrew", "secondChild": "Kayla"} print(list(zip(names1,names2)))
Take a minute to think about what the code output should be.
The zip() function stitches together the dictionary’s keys by default. So, running the code will give the following output:
[('mom', 'firstChild'), ('dad', 'secondChild')] |
However, the zip() function also allows you to “zip” the dictionary's values. To pull this off, you must call the values method on the dictionary objects as you pass them to zip().
Let’s continue with the earlier example and see how to do this:
names1 = {"mom":"Sonia", "dad":"Jesse"} names2 = {"firstChild":"Andrew", "secondChild": "Kayla"} print(list(zip(names1.values(),names2.values())))
The output of the code will now look like this:
[('Sonia', 'Andrew'), ('Jesse', 'Kayla')] |
Returning Different Data Structures from zip()
One of the nice things about zip() is that besides allowing you to pass different data structures, it enables you to export various data types.
Scroll back a bit and recall that the default output of zip() is a special zip object.
Earlier, we wrapped the zip() function in the list function to output a list. So, it shouldn’t surprise you that you can also use dict and tuple to return dictionaries and tuples.
Let’s have a look at an example:
names1 = ("Jack", "Theo", "Jose") names2 = ("Duncan", "Chris", "Anthony") print(tuple(zip(names1,names2)))
The result of running this code will look something like this:
(('Jack', 'Duncan'), ('Theo', 'Chris'), ('Jose', 'Anthony')) |
We can also work with the dict function, like so
names1 = ("Jack", "Theo", "Jose") names2 = ("Duncan", "Chris", "Anthony") print(dict(zip(names1,names2)))
The output will look like this:
{'Jack': 'Duncan', 'Theo': 'Chris', 'Jose': 'Anthony'} |
Using the zip() Function with More Than Two Arguments
All the examples in this guide include two iterables, but zip() can theoretically work with unlimited arguments. In this section, we’ll explore this capability of the zip() function.
Let's begin with defining three variables to create a zip object:
india_cities = ['Mumbai', 'Delhi', 'Bangalore'] africa_cities = ['Lagos', 'Cape Town', 'Tunis'] russia_cities = ['Moscow', 'Samara', 'Omsk']
Now, zipping the three objects together is as simple as passing them into zip(), like so:
print(list(zip(india_cities, africa_cities, russia_cities)))
The output of the code will look like this:
[('Mumbai', 'Lagos', 'Moscow'), ('Delhi', 'Cape Town', 'Samara'), ('Bangalore', 'Tunis', 'Omsk')] |
It should be clear that passing three arguments into zip() creates a new data structure. The structure’s elements are tuples having a length of 3.
Bear in mind that this extends to larger numbers. Putting it simply, if you pass “n” number of arguments into zip(), it’ll create a new data structure having tuple elements of length “n.”
How zip() Works with Arguments of Varying Lengths
In this guide, we have only touched upon how the zip() function works on data structures of the same length. First, we worked with structures with length two, and in the previous section, lists with length three.
But what happens when we pass arguments of different lengths to the zip() function?
The Python zip() function is quite flexible and accepts arguments with different lengths. Let’s explore this capability of the function in this section.
If you go back and open up the official Python documentation of the zip() function, the definition of the function will make something clear. The description of the function says:
“The iterator stops when the shortest input iterable is exhausted.”
In other words, the length of the zip() functions output will always equal the length of the smallest argument.
Here’s an example to verify this. Let's say we have two groups: one with men and one with women. You want to pair them up so they can take duo dance lessons – salsa, perhaps.
Now, let’s write some code with the zip() function and see how it can help us:
menGroup = ['Devin', 'Bruce', 'Jimmy', 'Eddie'] womenGroup = ['Gina', 'Mila', 'Friday'] print(list(zip(menGroup,womenGroup)))
It’s clear that the menGroup variable holds one more element than the womenGroup variable. So, we can expect the zip() function to drop the last element in the menGroup variable.
Running this code, we will get the following:
[('Devin', 'Gina'), ('Bruce', 'Mila'), ('Jimmy', 'Friday')] |
As expected, Eddie didn’t get a match for the salsa class. Let’s hope another lady joins in!
Looping Over Multiple Objects with the zip() Function
One of the most useful aspects of the zip() function is that it allows you to iterate over multiple objects parallelly. What’s more, the syntax involved is easy to remember and use!
Let’s understand looping with zip() with an example. Let’s say you two lists: one with students' names and the other with grades. Here’s what the code will look like:
studentList = ['Devin', 'Bruce', 'Jimmy'] gradeList = [97, 71, 83]
In this example, let’s assume that the list of students is ordered in the same position as their list of grades. Now, let’s write code to print every student’s name and their corresponding grade.
for student, grade in zip(studentList, gradeList): print(f"{student}'s grade is {grade}")
The result of running this code will look like this:
Devin's grade is 97 Bruce's grade is 71 Jimmy's grade is 83 |
Differences Between Python 3 and 2 in the zip() Function
It’s important to note that zip() works differently in Python 2 and 3. The zip() function returns a list of tuples in Python 2, and this list is truncated to the length of the shortest input iterable.
So, if you were to call a zip() function with zero arguments, the output will be an empty list like so:
zipping = zip(range(3), 'WXYZ') zipping # Holding the list object [(0, 'W'), (1, 'X'), (2, 'Y')] type(zipping) zipping = zip() # Creating empty list zipping []
As expected, calling zip() in the code above gives an output of a list of tuples. The list is truncated at “Y,” and calling zip() without arguments returns an empty list.
But this is not how zip() works in Python 3. In Python 3, calling zip() without arguments returns an iterator.
The iterator object yields tuples if the developer demands it. But the tuple can only be traversed once. The iteration ends with a StopIteration exception, which is raised when the shortest input iterable is exhausted.
Bear in mind that while an iterator is returned in Python 3, it is empty. Let’s take a look at an example:
zipping = zip(range(3), 'WXYZ') zipping # Holding an iterator type(zipping) list(zipping) zipping = zip() # Create an empty iterator zipping next(zipping)
Running this code will produce the output:
Traceback (most recent call last): File "<string>", line 7, in <module> StopIteration |
In this case, calling the zip() function returns an iterator. The first iteration is truncated at c, while the second raises the StopIteration exception.
The nice thing about Python 3 is that it can emulate the behavior of zip() in Python 2. It does this by wrapping the returned iterator in a call to list(). This way, it runs through the iterator, returning a list of tuples.
If you’re using Python 2, note that the passing long input iterables to the zip() function results in an unreasonably high memory consumption. So, if you need to pass long input iterables, use the itertools.izip(*iterables).
The function generates an iterator that aggregates the elements from the iterables. So, using it has the same effect as using zip() in Python 3. Let’s look at an example:
from itertools import izip zipped = izip(range(3), 'WXYZ') zipped list(zipped)
In the code above, the itertools.izip() function is called, and it creates an iterator. When the returned iterator is consumed with list(), it outputs a list of tuples, just like the zip() function in Python 3. When it reaches the end of the shortest input iterator, it stops.
If you use both Python 2 and 3, and prefer writing code in both that works the same way, here’s some code that you could use:
try: from itertools import izip as zip except ImportError: pass
In this code, the izip() function is available in itertools. So, in Python 2, the function will get imported under the alias zip. Else, the program will raise an ImportError exception, and this way you’ll know you’re using Python 3.
The code above enables you to use the zip() function in all the code you write. When the code is run, Python will automatically choose the right version.
Now that you understand the ins and out of the zip() function, it’s time to crack your knuckles, grab a coffee, and start coding real-world solutions!