Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Looping With Python enumerate()
Python’s enumerate()
function helps you with loops that require a counter by adding an index to each item in an iterable. This is particularly useful when you need both the index and value while iterating, such as listing items with their positions. You can also customize the starting index with the start
argument, offering flexibility in various scenarios.
By the end of this tutorial, you’ll understand that:
enumerate()
in Python adds a counter to an iterable and returns it as anenumerate
object, pairing each item with an index.enumerate()
accepts an iterable and an optionalstart
argument to set the initial index value.- The advantage of
enumerate()
is that it provides both index and value pairs without manual counter management. - You can use
zip()
or slicing as alternatives toenumerate()
for iterating multiple sequences or selecting specific elements.
Explore how to implement your own version of enumerate()
and discover alternative methods like zip()
and itertools
for more complex iteration patterns.
Get Your Code: Click here to download the free sample code you’ll use to explore how enumerate() works in Python.
Using Python’s enumerate()
There are situations when you need both the index and the value of items in an iterable. That’s when Python’s enumerate()
function can come in handy for you.
Python’s enumerate()
function adds a counter to an iterable and returns it in the form of an enumerate
object, which can be used directly in loops. For example, when you want to display the winners of a race, you might write:
>>> runners = ["Lenka", "Martina", "Gugu"]
>>> for winner in enumerate(runners):
... print(winner)
...
(0, 'Lenka')
(1, 'Martina')
(2, 'Gugu')
Since enumerate()
is a built-in function, you can use it right away without importing it. When you use enumerate()
in a for
loop, you get pairs of count and value from the iterable.
Note: If you’re coming from languages like C, Java, or JavaScript, you might be tempted to use range(len())
to get both the index and value in a loop. While this works in Python, enumerate()
is considered a more Pythonic and preferred approach.
In the example above, you’re storing the position and the runner’s name in a variable named winner
, which is a tuple
. To display the position and the name right away, you can unpack the tuple
when you define the for
loop:
>>> runners = ["Lenka", "Martina", "Gugu"]
>>> for position, name in enumerate(runners):
... print(position, name)
...
0 Lenka
1 Martina
2 Gugu
Just like the winner
variable earlier, you can name the unpacked loop variables whatever you want. You use position
and name
in this example, but they could just as easily be named i
and value
, or any other valid Python names.
When you use enumerate()
in a for
loop, you typically tell Python to use two variables: one for the count and one for the value itself. You’re able to do this by applying a Python concept called tuple unpacking.
Unpacking is the idea that a tuple—or another type of a Python sequence—can be split into several variables depending on the length of that sequence. For instance, you can unpack a tuple of two elements into two variables:
>>> cart_item = ("Wireless Mouse", 2)
>>> product_name, quantity_ordered = cart_item
>>> product_name
'Wireless Mouse'
>>> quantity_ordered
2
First, you create a tuple with two elements, "Wireless Mouse"
and 2
. Then, you unpack that tuple into product_name
and quantity_ordered
, which are each assigned one of the values from the tuple.
When you call enumerate()
and pass a sequence of values, Python returns an iterator. When you ask the iterator for its next value, it yields a tuple with two elements. The first element of the tuple is the count, and the second element is the value from the sequence that you passed:
>>> values = ["first", "second"]
>>> enumerate_instance = enumerate(values)
>>> print(enumerate_instance)
<enumerate at 0x7fe75d728180>
>>> next(enumerate_instance)
(0, 'first')
>>> next(enumerate_instance)
(1, 'second')
>>> next(enumerate_instance)
Traceback (most recent call last):
File "<python-input-5>", line 1, in <module>
next(enumerate_instance)
~~~~^^^^^^^^^^^^^^^
StopIteration
In this example, you create a Python list called values
with two elements, "first"
and "second"
. Then, you pass values
to enumerate()
and assign the return value to enumerate_instance
. When you print enumerate_instance
, you’ll see that it’s an enumerate
object with a particular memory address.
Then, you use Python’s built-in next()
to get the next value from enumerate_instance
. The first value that enumerate_instance
returns is a tuple with the count 0
and the first element from values
, which is "first"
.
Calling next()
again on enumerate_instance
yields another tuple—this time, with the count 1
and the second element from values
, "second"
. Finally, calling next()
one more time raises a StopIteration
exception since there are no more values to be returned from enumerate_instance
.
When an iterable is used in a for
loop, Python automatically calls next()
at the start of every iteration until StopIteration
is raised. Python assigns the value it retrieves from the iterable to the loop variable.
By default, the count of enumerate()
starts at 0
. This isn’t ideal when you want to display the winners of a race. Luckily, Python’s enumerate()
has one additional argument that you can use to control the starting value of the count.
When you call enumerate()
, you can use the start
argument to change the starting count:
>>> runners = ["Lenka", "Martina", "Gugu"]
>>> for position, name in enumerate(runners, start=1):
... print(position, name)
...
1 Lenka
2 Martina
3 Gugu
In this example, you pass start=1
, which initializes position
with the value 1
on the first loop iteration.
You should use enumerate()
anytime you need to use the count and an item in a loop. Keep in mind that enumerate()
increments the count by one on every iteration. However, this only slightly limits your flexibility. Since the count is a standard Python integer, you can use it in many ways.
Practicing With Python enumerate()
Now that you’ve explored the basics of enumerate()
, it’s time to practice using enumerate()
with some real-world examples.
Performing an Action Based on the Loop Step
Using conditional statements to process items can be a very powerful technique. Sometimes, you might need to perform an action on only the very first iteration of a loop, as shown in this example:
>>> tasks_by_priority = ["Pay Rent", "Clean Dishes", "Buy Milk"]
>>> for index, task in enumerate(tasks_by_priority):
... if index == 0:
... print(f"* {task.upper()}!")
... else:
... print(f"* {task}")
...
* PAY RENT!
* Clean Dishes
* Buy Milk
In this example, you use a list of tasks that are ordered by priority. The first task is your most important task, so you want to emphasize this task. When you loop through tasks_by_priority
, you print the task "Pay Rent"
in uppercase letters and with an exclamation mark (!
) because it’s the first item.
Calling enumerate()
allows you to control the flow of a loop based on the index of each item in a list, even if the values themselves aren’t important to your logic.
Getting Every Second Item of an Iterable
You can also combine mathematical operations with conditions for the index. For example, you might need to grab every second item from a Python string. You can do this by using enumerate()
:
>>> secret_message = "3LAigf7eq 5fhiOnpdDs2 Ra6 nwUalyo.9"
>>> message = ""
>>> for index, char in enumerate(secret_message):
... if index % 2:
... message += char
...
>>> print(message)
Here, you’ve received a mysterious string of characters in a secret_message
variable. To decipher the message, you loop through secret_message
with enumerate()
and access every character at an odd index.
To get only the characters at odd indexes, you check whether dividing the index
by 2 leaves a nonzero remainder—something that’s considered truthy in a Boolean context like this. That way, you add every second character from secret_message
to message
. Copy the code and run it to reveal what the message says!
Referencing Line Numbers in a File
Suppose you want to check lines of a file for certain formatting issues, such as trailing whitespace or tab characters, and report these with a line number. Once you stored the lines of the file in a list, you can traverse through them with enumerate()
:
>>> lines = [
... "This is a\tline",
... "This line is fine",
... "Another line with whitespace "
... ]
>>> for lineno, line in enumerate(lines, start=1):
... if "\t" in line:
... print(f"Line {lineno}: Contains a tab character.")
... if line.rstrip() != line:
... print(f"Line {lineno}: Contains trailing whitespace.")
...
Line 1: Contains a tab character.
Line 3: Contains trailing whitespace.
You use enumerate()
to loop over lines
and get the line number and each line of text. Then, you use two if
statements to check for out-of-place characters and print a message. By setting start
to 1
, lineno
isn’t a zero-based counter. That way, you get the correct line number of the lines in the file.
Instead of using consecutive if
statements, you could also leverage structural pattern matching. If you’re curious and would like to learn more, then check out the tutorial on structural pattern matching in Python.
Understanding Python enumerate()
In the last few sections, you saw examples of when and how to use enumerate()
to your advantage. Now that you’ve got a handle on the practical aspects of enumerate()
, you can learn more about how the function works internally.
To get a better sense of how enumerate()
works, you can implement your own version with Python. Your version of enumerate()
has two requirements. It should:
- Accept an iterable and a starting count value as arguments
- Send back a tuple with the current count value and the associated item from the iterable
One way to write a function that meets these specifications is given in the Python documentation:
>>> def my_enumerate(iterable, start=0):
... n = start
... for elem in iterable:
... yield n, elem
... n += 1
...
my_enumerate()
takes two arguments, iterable
and start
, where the default value of start
is 0
. Inside the function definition, you initialize n
to be the value of start
and run a for
loop over the iterable
.
For each elem
in iterable
, you yield
control back to the calling location and send back the current values of n
and elem
. Finally, you increment n
to get ready for the next iteration. You can see my_enumerate()
in action here:
>>> seasons = ["Spring", "Summer", "Fall", "Winter"]
>>> my_enumerate(seasons)
<generator object my_enumerate at 0x7f48d7a9ca50>
>>> list(my_enumerate(seasons))
[(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
>>> list(my_enumerate(seasons, start=1))
[(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]
First, you create a list of the four seasons to work with. Next, you show that calling my_enumerate()
with seasons
as the iterable
creates a generator object. This is because you use the yield
keyword to send values back to the caller.
Finally, you create two lists from my_enumerate()
: one where the start value is left as the default, 0
, and another in which start
is changed to 1
. In both cases, you end up with a list of tuples where the first element of each tuple is the count and the second element is the value from seasons
.
Although you can implement an equivalent function for enumerate()
in only a few lines of Python code, the actual code for enumerate()
is written in C. This means that it’s super fast and efficient.
Exploring Alternatives to enumerate()
By now, you’ve familiarized yourself with enumerate()
, and chances are that you have some use cases in mind. While enumerate()
is a powerful tool, there are several scenarios where other approaches are more appropriate. In this section, you’ll have a look at some examples where you could use enumerate()
to do the job, but it may not be the best choice.
Using Slicing Instead of enumerate()
Imagine you’ve been assigned the task of printing the first four letters of your favorite video game’s name. You’ve already learned that you can use if
conditions inside a for
loop that leverages enumerate()
. Equipped with this knowledge, you could approach this task like this:
>>> game_name = "Stardew Valley"
>>> first_letters = ""
>>> for i, letter in enumerate(game_name):
... if i < 4:
... first_letters += letter
... else:
... break
...
>>> print(first_letters)
Star
In this example, you create an empty string named first_letters
, which you fill up with the first four letters by looping through game_name
using enumerate()
. To just get the first four letters, you add the if
condition and exit the loop early with the break
keyword afterward.
That’s quite some code for a basic task like getting the first four letters of a string! Sure enough, there’s a better way, and it only takes one line of code:
>>> print(game_name[:4])
Star
This operation is known as slicing, and it’s the common way to get a portion of a sequence like a string.
When you only need specific ranges or patterns from a string, direct slicing is more efficient and concise than using enumerate()
with conditional logic.
Using zip()
for Multi-Sequence Iteration
Suppose you have a list of pets and another list of their owners. Your task is to print tuples where the first item is the pet’s name and the second item is the owner’s name. You could accomplish this with enumerate()
, but it’s not the best approach:
>>> pets = ["Leo", "Aubrey", "Frieda"]
>>> owners = ["Bartosz", "Sarah Jane", "Philipp"]
>>> for i, pet in enumerate(pets):
... print(f"{pet} & {owners[i]}")
...
Leo & Bartosz
Aubrey & Sarah Jane
Frieda & Philipp
In the example above, you’re using enumerate()
to get the index i
and pet
in your loop. Then, you use the index to access the item in owners
. The code works but it’s a bit complicated because you’re creating i
just to work with the index.
Instead, it would be cleaner to aggregate both lists with zip()
and loop through the iterator that the function returns:
>>> pets = ["Leo", "Aubrey", "Frieda"]
>>> owners = ["Bartosz", "Sarah Jane", "Philipp"]
>>> for pet, owner in zip(pets, owners):
... print(f"{pet} & {owner}")
...
Leo & Bartosz
Aubrey & Sarah Jane
Frieda & Philipp
While the result is the same, the intention of the for
loop definition is clearer when you use zip()
.
When you’re trying to iterate over multiple sequences simultaneously, it’s usually a good idea to leverage Python’s built-in function zip()
. If you want to learn more about zip()
, then you can check out Using the Python zip() Function for Parallel Iteration.
Using itertools
for Advanced Iteration Patterns
For more complex iteration patterns, the itertools
module often provides better alternatives than enumerate()
. For example, suppose you’re planning a road trip and want to tell a friend about your route.
First, start with enumerate()
and use the patterns you already know:
>>> cities = ["Graz", "Belgrade", "Warsaw", "Berlin"]
>>> for index, city in enumerate(cities[:-1]):
... print(f"{city} -> {cities[index + 1]}")
...
Graz -> Belgrade
Belgrade -> Warsaw
Warsaw -> Berlin
Notice how you must slice the cities
list that you pass into enumerate()
to ensure that you don’t loop until the end. Then, in the body of the for
loop, you’re accessing the cities as consecutive pairs.
For an advanced iteration pattern, like accessing consecutive pairs, it’s worth having a look what Python’s itertools
has to offer. In this case, itertools.pairwise()
does the trick:
>>> from itertools import pairwise
>>> cities = ["Graz", "Belgrade", "Warsaw", "Berlin"]
>>> for city, next_city in pairwise(cities):
... print(f"{city} -> {next_city}")
...
Graz -> Belgrade
Belgrade -> Warsaw
Warsaw -> Berlin
The itertools
module is part of Python’s standard library and not built-in like enumerate()
. That’s why you need to import itertools.pairwise()
before you can use it.
If you want to learn more about itertools
, then you can find a fantastic conversation in the Real Python Podcast Episode 252 with Rodrigo Girão Serrão.
Conclusion
Python’s enumerate()
lets you write Pythonic for
loops when you need a count and the value from an iterable. The significant advantage of enumerate()
is that it returns a tuple with the counter and value, so you don’t have to increment the counter yourself. It also gives you the option to change the starting value for the counter.
In this tutorial, you learned how to:
- Use Python’s
enumerate()
in yourfor
loops - Apply
enumerate()
in a few real-world examples - Get values from
enumerate()
using tuple unpacking - Implement your own equivalent function to
enumerate()
You’ve also explored alternative methods like zip()
and itertools
for more complex iteration patterns to help you decide when not to use enumerate()
.
Get Your Code: Click here to download the free sample code you’ll use to explore how enumerate() works in Python.
Frequently Asked Questions
Now that you have some experience with Python’s enumerate()
function, you can use the questions and answers below to check your understanding and recap what you’ve learned.
These FAQs are related to the most important concepts you’ve covered in this tutorial. Click the Show/Hide toggle beside each question to reveal the answer.
enumerate()
adds a counter to an iterable and returns it as an enumerate
object, which you can use in loops to access both the index and the value of items.
enumerate()
accepts an iterable and an optional start
argument that sets the starting value of the counter. The default value of start
is 0
.
enumerate()
provides both the index and the value of items in an iterable, removing the need to manually increment a counter.
For iterating over multiple sequences, use zip()
, and for more advanced iteration patterns, consider using the itertools
module.
Take the Quiz: Test your knowledge with our interactive “Python's enumerate()” quiz. You’ll receive a score upon completion to help you track your learning progress:
Interactive Quiz
Python's enumerate()Once you learn about for loops in Python, you know that using an index to access items in a sequence isn't very Pythonic. So what do you do when you need that index value? In this tutorial, you'll learn all about Python's built-in enumerate(), where it's used, and how you can emulate its behavior.
Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Looping With Python enumerate()