Looping over multiple items02 Mar 2018
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
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, 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)
zip function takes two sequnces, and creates one sequence containing tuples of the pairs of elements. So
[('Anne', '234'), ('Bill', '236'), ('Carol', '230'), ('Dave', '229')]
See what has happened here?
- the first element of
pairsis a tuple created from the first element of
namesand the first element of
- the second element of
pairsis a tuple created from the second element of
namesand the second element of
- 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!)
zipdoesn’t actually create a list, it creates an iterator. The values are created lazily each time the
forloop asks for the next value.
Now we get to the loop:
for pair in pairs:
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
pair will hold
The first line of the loop block looks like this:
name, number = pair
This is simply tuple unpacking - Python shorthand for:
name = pair number = pair
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
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 it to loop through both sequences at the same time.
More about zip()
zip works with any sequence or iterable.
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
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 need to start with an extra zero.
How would we update our
numbers list? Here is how we could use
numbers = ['234', '236', '230', '229'] for i, number in enumerate(numbers): numbers[i] = '0' + number print(numbers)
As you might have guessed,
enumerate work 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
enumerate creates this sequence of tuples:
[(0, '234'), (1, '236'), (2, '230'), (3, '229')]
enumerate sparingly. For example, if you wanted to create the updated numbers in a new list (let’s call it
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