Looping with indexes

Share
Copied to clipboard.
Series: Looping
Trey Hunner smiling in a t-shirt against a yellow wall
Trey Hunner
4 min. read 3 min. video Python 3.8—3.12

If you've used another programming language before, you've probably used indexes while looping. Often when you're trying to loop with indexes in Python, you'll find that you actually care about counting upward as you're looping, not actual indexes.

Using a counter to keep track of indices

Let's say we have a variable, favorite_fruits that points to a list of strings:

>>> favorite_fruits = ["jujube", "pear", "watermelon", "apple", "blueberry"]

We're looping over our favorite_fruits list here:

>>> n = 1
>>> for fruit in favorite_fruits:
...     print(n, fruit)
...     n += 1
...
1 jujube
2 pear
3 watermelon
4 apple
5 blueberry

We're printing out n and fruit (which represents each of our fruits). The n variable is a counter that we're keeping track of ourselves. We start n at 1 and we're incrementing it by 1 in each iteration of our loop.

Instead of keeping track of counter ourselves, how about thinking in terms of indices and using range(len(favorite_fruits)) to grab each of the indexes of all the items in this list:

>>> favorite_fruits = ["jujube", "pear", "watermelon", "apple", "blueberry"]
>>>
>>> for i in range(len(favorite_fruits)):
...     print(i+1, favorite_fruits[i])
...
1 jujube
2 pear
3 watermelon
4 apple
5 blueberry

We’re using i to index the list manually and add one to each of these indices. This gives us the same result as before.

I wouldn't recommend doing this because the official Python style guide, PEP 8, recommends against it and instead of using range of len, there is usually one of two techniques you'll want to use.

We can entirely get rid of this and instead say for fruit in favorite_fruits:

>>> favorite_fruits = ["jujube", "pear", "watermelon", "apple", "blueberry"]
>>>
>>> for fruit in favorite_fruits:
...     print(fruit)
...
jujube
pear
watermelon
apple
blueberry

Here we've decided we don't need any counter in our loop at all.

If you do decide you actually need some kind of counting as you're looping, you'll want to use the built-in enumerate function.

enumerate returns both index and value

Python's enumerate function is for counting upward as you loop over an iterable.

If we give enumerate our iterable (our favorite_fruits list in this case) and an optional start value (which defaults to 0):

>>> favorite_fruits = ["jujube", "pear", "watermelon", "apple", "blueberry"]
>>>
>>> for fruit in enumerate(favorite_fruits):
...     print(fruit)
...
(0, 'jujube')
(1, 'pear')
(2, 'watermelon')
(3, 'apple')
(4, 'blueberry')

We'll get an iterable back that can be looped over to get tuples containing numbers counting upward along with each item in the iterable that we passed to enumerate.

If we want to start counting at a different number, we can set the start keyword argument when calling enumerate:

>>> favorite_fruits = ["jujube", "pear", "watermelon", "apple", "blueberry"]
>>>
>>> for fruit in enumerate(favorite_fruits, start=1):
...     print(fruit)
...
(1, 'jujube')
(2, 'pear')
(3, 'watermelon')
(4, 'apple')
(5, 'blueberry')

Tuple unpacking

As we loop over enumerate, it's giving us back two-item tuples:

>>> favorite_fruits = ["jujube", "pear", "watermelon", "apple", "blueberry"]
>>>
>>> for item in enumerate(favorite_fruits, start=1):
...     print(item)
...
(1, 'jujube')
(2, 'pear')
(3, 'watermelon')
(4, 'apple')
(5, 'blueberry')

Each tuple has a number (counting upward) and the next item from our favorite_fruits iterable.

We could index this tuple, using item[0] and item[1], to grab these two values:

>>> favorite_fruits = ["jujube", "pear", "watermelon", "apple", "blueberry"]
>>>
>>> for item in enumerate(favorite_list, start=1):
...     print(item[0], item[1])
...
1 jujube
2 pear
3 watermelon
4 apple
5 blueberry

But a better way is to use tuple unpacking to give each of these two values a name:

>>> favorite_fruits = ["jujube", "pear", "watermelon", "apple", "blueberry"]
>>>
>>> for item in enumerate(favorite_fruits, start=1):
...     n, fruit = item
...     print(n, item)
...
1 (1, 'jujube')
2 (2, 'pear')
3 (3, 'watermelon')
4 (4, 'apple')
5 (5, 'blueberry')

The number (n) and that actual item (fruit) are both important here. If something is important, it deserves a name.

Tuple unpacking is allowed anywhere an assignment happens, and that for line has an assignment happening within it, so we can unpack at the same time as we loop:

>>> favorite_fruits = ["jujube", "pear", "watermelon", "apple", "blueberry"]
>>>
>>> for n, fruit in enumerate(favorite_fruits, start=1):
...     print(n, fruit)
...
1 jujube
2 pear
3 watermelon
4 apple
5 blueberry

This is the most common way you'll see enumerate used: you'll almost always see tuple unpacking used whenever enumerate is used.

Use enumerate to count upward while looping

If you think you need to loop with indices, first, ask yourself a question: do I even need some kind of counter as I'm looping? If you don't need a counter, just use a for loop without any frills. But if you do need to count upward while looping, you can use Python's built-in enumerate function to help you do that counting.

Series: Looping

Unlike, JavaScript, C, Java, and many other programming languages we don't have traditional C-style for loops. Our for loops in Python don't have indexes.

This small distinction makes for some big differences in the way we loop in Python.

To track your progress on this Python Morsels topic trail, sign in or sign up.

0%
Concepts Beyond Intro to Python

Intro to Python courses often skip over some fundamental Python concepts.

Sign up below and I'll share ideas new Pythonistas often overlook.