Looping over multiple items (old article)
Categories: python language intermediate python
This article is old. Please refer to the newer articles:
Sometimes you need to use a for
loop to loop over more than one list at the same time, within the same loop. For example, suppose
we have a list of employee names and a separate list of phone extension numbers:
names = ['Anne', 'Bill', 'Carol', 'Dave']
numbers = ['234', '236', '230', '229']
What if you want to print a directory of numbers, something like:
Anne 234
Bill 236
Carol 230
Dave 229
You could do it the ugly way, with a loop index:
for i in range(4):
print(names[i] + ' ' + numbers[i])
That's ok, it is what you might do in other languages, but Python has a better way, using the zip
function.
zip
He is how to solve the problem in a more Pythonic way:
for name, number in zip(names, numbers):
print(name + ' ' + number)
You can probably guess what this code is doing - the two lists are being joined in some way. We then somehow loop over the list using two variables.
Most of the time you will just use and recognise this as a common idiom - you know what it does, and you don't necessarily have to worry about how it works. But in fact, it isn't that complicated.
How zip() works
To understand this code, we will first expand it out a bit. You should never write actual code like the code below, it is just too long-winded.
Any experienced Python programmer will know how zip
works in a loop. But to aid understanding we will write it longhand:
pairs = zip(names, numbers)
for pair in pairs:
name, number = pair
print(name + ' ' + number)
Taking the first line:
pairs = zip(names, numbers)
The zip
function takes two sequences, and creates one sequence containing tuples of the pairs of elements. So pairs
will contain:
[('Anne', '234'), ('Bill', '236'), ('Carol', '230'), ('Dave', '229')]
See what has happened here?
- The first element of
pairs
is a tuple created from the first element ofnames
and the first element ofnumbers
. - The second element of
pairs
is a tuple created from the second element ofnames
and the second element ofnumbers
. - and so on...
This is the zip operation - it joins the two lists, a bit like zipping up a coat (and nothing to do with ZIP compression!)
zip
doesn't actually create a list, it creates an iterator. The values are created lazily each time thefor
loop asks for the next value.
Now we get to the loop:
for pair in pairs:
Since pairs
is a sequence of tuples, on each pass through the loop, pair
will hold a tuple value. So on the first pass through the
loop, pair
will hold ('Anne', '234')
.
The first line of the loop block looks like this:
name, number = pair
This is simply tuple unpacking - Python shorthand for:
name = pair[0]
number = pair[1]
So looking at the first three lines of our example code:
pairs = zip(names, numbers)
for pair in pairs:
name, number = pair
All we have really done is removed the unnecessary variables pairs
and pair
, and compressed the three lines into one:
for name, number in zip(names, numbers):
You don't need to go through all this work every time you use zip
, just remember that you can zip two (or more) sequences
together, and then read them into separate variables, and use them to loop through both sequences at the same time.
More about zip()
zip
works with any sequence or iterable.
You can zip
any number of sequences. If you zip
three sequences, for example, each tuple in the result will have three elements.
If the input sequences have different lengths, the output will be the same length as the shortest input sequence. The extra elements at the end of the longer sequence(s) will be ignored.
Accessing the loop count using enumerate
Even though it is often best to avoid loop counters, there are times when they can be useful. Fortunately, you don't have to forget everything
you have learnt so far - you can use the enumerate
function.
A common use of enumerate
is if you want to loop through a list modifying the elements in place (that is, you don't want to create a
new array). For example, suppose we get a new office telephone system, and each phone extension now needs to start with an extra zero.
How would we update our numbers
list? Here is how we could use enumerate
:
numbers = ['234', '236', '230', '229']
for i, number in enumerate(numbers):
numbers[i] = '0' + number
print(numbers)
As you might have guessed, enumerate
works a little like zip
, except that it only takes a single sequence as input. It creates a
sequence of tuples where the first element is the count value. So for the numbers
list, enumerate
creates this sequence of tuples:
(0, '234'), (1, '236'), (2, '230'), (3, '229')
Use enumerate
sparingly. For example, if you wanted to create the updated numbers in a new list (let's call it new_numbers
), you
could do it like this:
numbers = ['234', '236', '230', '229']
new_numbers = []
for number in numbers:
new_numbers.append('0' + number)
Or you could use a list comprehension to do it in one line.
In summary, if you need to loop over two or more sequences in parallel, consider using zip()
to avoid the need for a loop counter.
If you really need a loop counter, for example, to update the existing element of a list, consider using enumerate()
.
See also
- List comprehensions
- Objects and variables
- Objects and identity
- Immutable objects
- Global variables
- Data types
- Lists vs tuples
- Sequences
- Named tuples
- Operators
- Short circuit evaluation
- Walrus Operator
- For loops
- For loop using range vs iterables
- Changing the loop order
- Using enumerate in a for loop
- Using zip in a for loop
- Looping over selected items
- Functions
- Declaring functions
- Calling functions
- Function objects and lambdas
- Function decorators
- With statements
- Exception handling
- String functions
- Built-in functions
- Optimisation
- Optimisation good practice
- Low level code optimisation
- Structural optimisation
Join the PythonInformer Newsletter
Sign up using this form to receive an email when new content is added:
Popular tags
2d arrays abstract data type alignment and angle animation arc array arrays bar chart bar style behavioural pattern bezier curve built-in function callable object chain circle classes clipping close closure cmyk colour combinations comparison operator comprehension context context manager conversion count creational pattern data science data types decorator design pattern device space dictionary drawing duck typing efficiency ellipse else encryption enumerate fill filter font font style for loop formula function function composition function plot functools game development generativepy tutorial generator geometry gif global variable gradient greyscale higher order function hsl html image image processing imagesurface immutable object in operator index inner function input installing iter iterable iterator itertools join l system lambda function latex len lerp line line plot line style linear gradient linspace list list comprehension logical operator lru_cache magic method mandelbrot mandelbrot set map marker style matplotlib monad mutability named parameter numeric python numpy object open operator optimisation optional parameter or pandas partial application path pattern permutations pie chart pil pillow polygon pong positional parameter print product programming paradigms programming techniques pure function python standard library radial gradient range recipes rectangle recursion reduce regular polygon repeat rgb rotation roundrect scaling scatter plot scipy sector segment sequence setup shape singleton slice slicing sound spirograph sprite square str stream string stroke structural pattern subpath symmetric encryption template tex text text metrics tinkerbell fractal transform translation transparency triangle truthy value tuple turtle unpacking user space vectorisation webserver website while loop zip zip_longest