🔥 NEW COURSE: The Data Engineering Bootcamp 🔥
Use Code: DATAENG26 to get 26% OFF any membership. Expires soon 👇

Beginner’s Guide to enumerate() in Python

Travis Cuzick
Travis Cuzick
hero image

Have you ever been looping through a list in Python and everything’s going fine, right up until the point where you realize you need the position of each item too?

Maybe you want to number them, or perhaps you need the index for something else, but that’s when things get a little complicated - especially if you’re looping with range() and manually indexing into the list.

The good news though is that Python has a built-in tool for this exact situation, and it’s called enumerate(). This function is super simple to use, and once you understand how it works, you’ll see all sorts of opportunities to improve your code with it.

In this guide I’ll break down how it works, when to use it, when to skip it, and how to get past the common mistakes.

Let’s dive in…

Sidenote: If you find any of this confusing, or simply want a deep dive into Python, check out Andrei's Python Coding course taken by 200,000+ people:

It’ll take you from an absolute beginner to understanding everything you need to be hired ASAP.

Alternatively, if you're already pretty good at Python and want to build some interesting and useful projects, why not check out my course on Python Automation:

It'll show you how to automate all of the boring or repetitive tasks in your life - and makes for some pretty standout portfolio projects as well!

With all that out of the way, let's get into this 5-minute tutorial!

What is enumerate() and how does it work?

enumerate() is a built-in Python function that lets you loop through any

That’s all it does. 

However, it turns out that being able to get both the value and the index in a loop is something you’ll need to do more often than you’d expect.

For example

Imagine you’re writing a small terminal-based to-do list app. 

Nothing too fancy, just something that prints out a list of tasks and numbers them so users can choose one to mark as complete:

0: Walk the dog  
1: Take out the trash  
2: Buy milk

So now you’ve got a list of tasks, and you want to print each one with its position. However, if you haven’t seen enumerate() before, the obvious first solution might be something like this:

tasks = ['Walk the dog', 'Take out the trash', 'Buy milk']
for i in range(len(tasks)):
    print(f"{i}: {tasks[i]}")
This method works but it’s not ideal, because you’re looping through one thing (a range of numbers) while reaching into another (the list), which adds friction and makes the loop harder to read.

Here’s the cleaner solution using enumerate():

for i, task in enumerate(tasks):
    print(f"{i}: {task}")

So what’s happening here?

  • enumerate(tasks) gives you a pair which is the index and the item

  • Python lets you unpack both in one line: i gets the index, task gets the value

  • No more range(len(...)) gymnastics or reaching into the list by hand

And best of all, when you glance at that loop, it immediately tells you what it’s doing, because it’s clean, clear, and hard to misinterpret.

Simple right?

So let’s look at a few more examples of enumerate() in action, applied to scenarios where it really shines.

How to select items from a list with enumerate()

So let's take this a step further.

In the example I just gave, we printed a task list using enumerate() and saw why numbering the items in an iterable can make them easier to work with. However,

First, here’s the list:

tasks = ['Walk the dog', 'Take out the trash', 'Buy milk']
for i, task in enumerate(tasks):
    print(f"{i}: {task}")

That gives us:

0: Walk the dog
1: Take out the trash
2: Buy milk

Now let’s ask the user which task they completed:

choice = int(input("Which task did you complete? "))
print(f"You marked task {choice} as done: {tasks[choice]}")

This works cleanly because the number you printed next to each task is the same as its actual position in the list. So when the user types 1, it pulls out the correct task with tasks[1].

Handy right?

And sure, if you’d written this loop with range(len(...)), you’d get the same result, but it would take more effort and more code. Using enumerate() just keeps everything lined up without you needing to think about it.

These sorts of things come up all the time, so being able to simplify them can improve your quality of life in a major way.

How to start numbering at 1 (or any number) with enumerate()

By default, enumerate() starts counting from 0, which makes sense if you’ve been coding for a while.

The problem of course is that the average user on the street is going to assume that lists start at 1. So making your lists start at 0 instead can be confusing for them. 

The good news is we can fix this by customizing which number our list starts at, in this case to 1.

So instead of:

0: Walk the dog
1: Take out the trash
2: Buy milk

We can have:

1: Walk the dog
2: Take out the trash
3: Buy milk

And the solution is actually super simple. You just need to tell enumerate() where to start:

for i, task in enumerate(tasks, start=1):
    print(f"{i}: {task}")

Easy right? The loop works exactly the same as before, except that now the numbers begin at 1. That makes for a much friendlier user experience, and without you needing to write any extra logic.

You can also use other numbers if you have a specific reason. 

For example

  • start=100 if you're assigning ticket numbers

  • start=42 if you're continuing a list from earlier

  • start=2 if the first item is reserved

Just remember though, that if you’re going to use that modified/offset index number to look up its associated item later, you may need to adjust it back:

choice = int(input("Which task did you complete? "))
index = choice - 1  # adjust back
print(f"You marked task {choice} as done: {tasks[index]}")

This way you keep the output of your program user-friendly, while still working safely with Python’s 0-based indexing behind the scenes.

How to show progress while looping through a list

Sometimes you’re processing a list of items such as files, tasks, or emails, and you want to display an indication of your program’s progress while it runs.

For example, maybe the program takes a few seconds to process each item, and you just want to print a line per item to keep track of what’s happening.

This is another situation where enumerate() can be incredibly helpful.

For example

Let’s say you’re working through a list of files:

files = ['invoice1.pdf', 'invoice2.pdf', 'invoice3.pdf']

And you want to print something like:

Processing item 1 of 3: invoice1.pdf
Processing item 2 of 3: invoice2.pdf
Processing item 3 of 3: invoice3.pdf
Here’s how to do it:

for i, filename in enumerate(files, start=1):
    print(f"Processing item {i} of {len(files)}: {filename}")

Because enumerate() gives you both the number and the file name, you don’t need to manage a counter or worry about off-by-one errors. It just works.

Now to be sure,  you could technically write the same thing with a manual counter:

i = 1
for filename in files:
    print(f"Processing item {i} of {len(files)}: {filename}")
    i += 1

However, this solution requires you to manage that i yourself, which means adding unnecessary moving parts to the code. But enumerate() allows you to keep your code cleaner in this situation, taking away one more thing that could go wrong.

This pattern is great for:

  • Long-running scripts where feedback helps

  • Debugging which item caused an error

  • Giving the user a sense of progress

It’s a small thing that can nonetheless make your scripts feel more thoughtful, especially when they involve looping through a long list.

User experience matters, and knowing that the code is actually running is always helpful!

How to compare two lists side by side

Another place enumerate() really helps is when you're comparing two lists. Perhaps you’re checking someone’s answers against the correct ones, or comparing before-and-after versions of something.

For example

Let’s say you're building a quick quiz checker and you’ve got two lists: one containing the submitted answers, and one containing the correct ones.

submitted = ['A', 'B', 'C', 'D']
correct = ['A', 'C', 'C', 'D']

Now you want to go through each answer, compare it to the correct one, and print which questions were wrong. Here’s how you might do it:

for i, answer in enumerate(submitted):
    if answer != correct[i]:
        print(f"Question {i + 1}: Incorrect (you answered {answer}, correct was {correct[i]})")

This works because enumerate() gives you the index (which matches the question number)

Without enumerate(), you’d be stuck using range(len(...)):

for i in range(len(submitted)):
    if submitted[i] != correct[i]:
        ...

It works, but it’s definitely not as readable; you’re doing more indexing, and mixing logic with structure as well. With enumerate(), the loop does exactly what it says, and gives you the index and the item, one at a time.

This same pattern comes up a lot:

  • Comparing two lists of data

  • Checking changes between versions

  • Validating input vs. expected values

Anytime you need to track what changed and where, enumerate() makes the loop clearer and your code easier to follow.

How to modify items in a list using their index

Sometimes you don’t just want to read from a list, but also to actually update it. Perhaps you're formatting the text, applying some sort of transformation, or even changing certain items based on a logical rule.

The thing is though, if you write a loop like the one below you won’t actually change the list.

for item in my_list:
    item = item.upper()

That’s because name is just a temporary variable. It holds a

So if you want to change the list itself and not just its copy, you need to assign a new value directly into the original list. And to do that, you’ll need the value’s index.

This works like so:

names = ['alice', 'bob', 'chris']

for i, name in enumerate(names):
    names[i] = name.upper()

After this loop, names becomes:

['ALICE', 'BOB', 'CHRIS']

This is a really common use of enumerate() because it lets you write a clean loop that’s easy to read, yet still gives you access to the index when you need to make changes.

Sure, you could write it like this:

for i in range(len(names)):

    names[i] = names[i].upper()

But now you're reaching into the list twice, to say nothing of how much more awkward the loop is to follow. So enumerate() just makes things work and look much smoother.

So whenever you're looping through a list and want to update the original values, this is your go-to pattern.

Common mistakes when using enumerate() and how to fix them

Even though the mechanics of enumerate() are fairly easy to grasp, there are a few common mistakes that trip people up early on when using it. Most of these come from not fully understanding what Python is actually doing under the hood, and they can lead to confusing errors, or  - even worse - silent bugs.

So let’s break down these mistakes and how to fix them.

Mistake #1. Forgetting to unpack the index and item

This is the most common mistake people make with enumerate(), especially if they’ve only seen it used once or twice. You expect it to work like a normal for loop, but instead of giving you just one item at a time, it gives you two: the index and the value.

If you forget to unpack those two parts, your loop behaves in weird ways which can result in confusing output, or even an error.

For example

You might expect this to print just the fruit names:

for item in enumerate(['apple', 'banana']):

    print(item)

The thing is, it actually prints:

(0, 'apple')
(1, 'banana')

That’s because enumerate() always returns a pair, (otherwise known as a ‘tuple'),  containing the index and the item. 

So if you only give Python one variable, it puts the whole pair into that one variable, which becomes a problem if you try to use that variable as though it’s just the item.

For example

for item in enumerate(['apple', 'banana']):

    print(item.upper())  # Error!

This crashes, because item is actually a tuple like (0, 'apple'), and the .upper() method doesn’t work on tuples.

The fix? Just tell Python how to unpack both values:

for i, fruit in enumerate(['apple', 'banana']):

    print(f"{i}: {fruit.upper()}")

Easy right? Now you’re getting exactly what you expect: i is the index, fruit is the string, and you can use both clearly inside the loop.

Once you understand that enumerate() gives you two things, it becomes much easier to work with.

Mistake #2. Using enumerate() on something that isn’t iterable

This one usually pops up when you're moving fast or not quite sure what enumerate() expects, will result in Python throwing up an error like:

So what’s actually going wrong here?

Well, as we know from the last mistake, enumerate() is designed to loop over something that has multiple items, such as a list, a string, or a tuple. In Python terms, that’s called an

But if you accidentally pass in something that isn’t iterable, like an integer, Python doesn’t know what to do.

For example

for i, item in enumerate(42):

    print(i, item)

That would crash immediately because you can’t enumerate over a single number. There’s nothing to actually loop through.

Same thing happens if you try:

for i, item in enumerate(None):

    ...

Again, Python doesn’t know how to loop through None, so it gives you a TypeError.

This mistake usually comes from one of two places:

  • You meant to pass in a list but accidentally passed a single value.

  • You’re calling a function that sometimes returns None, and didn’t check before attempting to iterate over that return value.

So how do we prevent these sorts of issues?

Well, the solution depends on what you’re trying to do, so here’s a few options:

If you meant to loop through a single item, wrap it in a list:

value = 42

for i, item in enumerate([value]):

    print(i, item)

Or, if the thing you’re looping through might be None, add a check:

data = get_data()  # Might return None

if data:
    for i, item in enumerate(data):
        ...
And if you want to be extra safe, you can also use a fallback:

for i, item in enumerate(data or []):
    ...

That way, if data is None, nothing happens. There’s no meaningful output but also no crashes or other problems either.

Mistake #3. Modifying the loop variable and expecting the list to change

This one is sneaky because it looks like your code runs fine, but then nothing actually happens.

For example

Let’s say you want to capitalize every task in a list, and so you write something like:

tasks = ['walk the dog', 'take out the trash', 'buy milk']

for task in tasks:
    task = task.title()

The issue is that when you run this code, the original list doesn’t change at all - it’s still lowercase with no capitalization.

So what gives?

Well, when you write for task in tasks, Python is giving you a

This mistake trips up a lot of people because the code looks like it should work, and even runs error-free. But unless you leverage the indexes of the items you’re iterating over, the original list stays untouched.

Specifically, to actually update the list you'll need to use those indexes to assign the new values back to the correct locations within the list . 

Here’s the fix:

for i, task in enumerate(tasks):

    tasks[i] = task.title()

Now you're telling Python: “Here’s the position of this task. Go back and update it in the list.”

And after this runs, the list really IS updated:

['Walk The Dog', 'Take Out The Trash', 'Buy Milk']

Sure, you could also do this with range(len(tasks)), but as usual enumerate() reads better and keeps everything in one place.

This pattern is useful any time you need to:

  • Clean up or transform a list

  • Modify specific items based on rules

  • Apply formatting or updates in place

Just remember: if you want to modify the list you’re iterating over, you need the indexes of the items in that list. 

And luckily for us, enumerate() makes accessing those indexes and the values associated with them super simple.

What are you waiting for? Give enumerate() a try in your own code today

So now that you’ve seen what enumerate() does, how it works, and where it’s useful, it’s time to try it out yourself. As with so many things, the best way to learn it is to start using it!

Next time you're writing a loop and you catch yourself reaching for range(len(...)), stop and ask: “Would enumerate() make this easier to read or write?”

It probably will.

You can also use it to number items, keep track of position, or update values in a list — all without adding unnecessary complexity to your code. Once it clicks, you’ll find yourself using it all the time.

P.S.

Don’t forget, if you want to learn more and dive deep into Python, then be sure to check out Andrei's Complete Python Developer course

It’ll take you from an absolute beginner and teach you everything you need to get hired ASAP and ace the tech interview.

This is the only Python course you need if you want to go from complete Python beginner to getting hired as a Python Developer this year!

Alternatively, if you're already pretty good at Python and want to build some interesting and useful projects, why not check out my course on Python Automation:

It'll show you how to automate all of the boring or repetitive tasks in your life, and makes for some pretty stand out portfolio projects!

Plus, as part of your membership, you'll get access to both of these courses and others, and be able to join me and 1,000s of other people (some who are alumni mentors and others who are taking the same courses that you will be) in the ZTM Discord.

Ask questions, help others, or just network with other Python Developers, students, and tech professionals.

More Python Tutorials

If you enjoyed this post, check out my other Python tutorials:

More from Zero To Mastery

Beginner’s Guide To Python Automation Scripts (With Code Examples) preview
Beginner’s Guide To Python Automation Scripts (With Code Examples)
24 min read

Use Python automation scripts to save time, reduce errors, and boost productivity. Perfect for all skill levels. Work smarter, not harder!

How to Become a Full-Stack Web Developer & Get Hired in 2025 preview
How to Become a Full-Stack Web Developer & Get Hired in 2025
32 min read

Learn everything you need to know to become a Full-Stack Web Developer, as well as how to get hired as one in 2025 with this step-by-step guide!

Potential Python Interview Questions preview
Potential Python Interview Questions
15 min read

Are you taking a coding interview with Python? On rare occasions, you may be asked broad language questions before the technical interview: Here's 25 of them.