What is Enumerate in Python?


You’ve been writing for-loops like this:

dogs = ['corgi', 'pug', 'shih tzu']
for dog in dogs:
    print(dog)

But you soon realized that you need to print the index of each element in the loop, so you instinctively go to this, outputting the following:

dogs = ['corgi', 'pug', 'shih tzu']
for index in range(len(dogs)):
    print(f'{index}: {dogs[index]}')
0: corgi
1: pug
2: shih tzu

The printed statement follows the f-String syntax, a new string formatting technique released in Python 3.6.

So this method functions as intended, but we actually don’t have to manually generate the index.

enumerate() can do that for us.

Basic Usage: enumerate()

The enumerate() structure is as follows:

for index, element in enumerate(array):
    # element is accessible here
    # element index is accessible here

Here is how we can use enumerate to perform the same function as above with a side-by-side comparison:

dogs = ['corgi', 'pug', 'shih tzu']
for index, dog in enumerate(dogs):
    print(f'{index}: {dog}')
dogs = ['corgi', 'pug', 'shih tzu']
for index in range(len(dogs)):
    print(f'{index}: {dogs[index]}')

As you can see, there’s no need for list indexing. Inside each loop, we have access to the list element and the index of that element in the list.

Enumerating Over Different Types

Lists

This is simply the example from earlier:

dogs = ['corgi', 'pug', 'shih tzu'] # list
for index, dog in enumerate(dogs):
    print(f'{index}: {dog}')
0: corgi
1: pug
2: shih tzu

Tuples

This is pretty much the exact same as with a list.

dogs = ('corgi', 'pug', 'shih tzu') # tuple
for index, dog in enumerate(dogs):
    print(f'{index}: {dog}')
0: corgi
1: pug
2: shih tzu

String

This is pretty much the exact same as with a tuple.

dog = 'pug' # string
for index, char in enumerate(dog):
    print(f'{index}: {char}')
0: p
1: u
2: g

In this case, you can imagine a string as an array of characters, which is why we can traverse them like arrays.

List of Tuples

Let’s say we have a list of tuples, like so:

dogs = [('corgi', 5), ('pug', 3), ('shih tzu', 8)]

Essentially, each dog is associated with a number, let’s say, his or her age. We can enumerate as follows:

for index, value in enumerate(dogs):
    dog = value[0]
    age = value[1]
    print(f'{index}: {dog}: {age}')
0: corgi: 5
1: pug: 3
2: shih tzu: 8

But we can simplify this by assigning multiple variables (dog, age) on the same line.

for index, value in enumerate(dogs):
    dog, age = value[0], value[1]
    print(f'{index}: {dog}: {age}')

We can further simplify using what is known as tuple unpacking.

for index, value in enumerate(dogs):
    (dog, age) = value
    print(f'{index}: {dog}: {age}')

Lastly, we can move that tuple unpacking into the for loop structure itself, leaving us with:

for index, (dog, age) in enumerate(dogs):
    print(f'{index}: {dog}: {age}')

Changing the Starting Index

You can also change the starting index of your for-loop to whatever you’d like.

dogs = ['corgi', 'pug', 'shih tzu'] # list
for index, dog in enumerate(dogs, 1):
    print(f'{index}: {dog}')
1: corgi
2: pug
3: shih tzu

The most common use case is to have a one-based index instead of the normal zero-based index.

If you need to start counting from 1 instead of 0, this is a great Pythonic way to do it.

Theoretically, you can start counting from whatever number you want: 5, 100, -47, you name it.

How Does enumerate() Actually Work?

Great question.

If we were to print out the output of the enumerate() function, what do we get?

dogs = ['corgi', 'pug', 'shih tzu'] # list
enumerate(dogs) # <enumerate object at 0x7f9c6232bf50&gt;

It’s an enumerate object, which is an iterator.

Right when the iterator is created, the iterator only knows the next value in the list. It does not know every single item in the list. It retrieves the next item when it is requested.

That is why we can’t print out every single item at once. To do so, we can convert the iterator to a list:

list(enumerate(dogs)) # [(0, 'corgi'), (1, 'pug'), (2, 'shih tzu')]

So each element is a tuple with the index and the value.

That is why the structure of the for-loop is:

for index, value in enumerate(array):

Because each iteration requires tuple unpacking.

You can think of it as if we are iterating through a list of tuples, unpacking in order to isolate the next index and the value.

for index, value in [(0, 'corgi'), (1, 'pug'), (2, 'shih tzu')]:

Remember, however, that this is not how the iterator functions under the hood. There is no list of tuples. It does not know every entry in the list as it appears in this code above.

The iterator retrieves the next tuple in the next iteration. In other words, the outputs are generated lazily, or one-by-one.