Looping over multiple items (old article)

By Martin McBride, 2018-03-02
Tags: zip enumerate for loop
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 of names and the first element of numbers.
  • The second element of pairs is a tuple created from the second element of names and the second element of numbers.
  • 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 the for 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

If you found this article useful, you might be interested in the book NumPy Recipes or other books by the same author.

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