What Is Python’s enumerate() Function and How Do You Use It?
PythonA quick guide to looping with a counter the Pythonic way, with practical examples and gotchas.

What it is
Python’s enumerate()
is a built-in that adds a running index to any iterable, yielding pairs of (index, value)
. It returns a lightweight “enumerate object” you can iterate over directly or cast to a list. See the official documentation for the full signature.
Why it matters
- Removes error-prone manual counters in loops.
- Stays readable and “Pythonic” compared to
range(len(...))
. - Works lazily, so it’s efficient for large iterables.
How it works
By default, the index starts at 0. You can change that with the optional start
argument.
# Basic usage
runners = ["Lenka", "Martina", "Gugu"]
for i, name in enumerate(runners):
print(i, name)
# 0 Lenka
# 1 Martina
# 2 Gugu
# One-based indexing
for position, name in enumerate(runners, start=1):
print(position, name)
# 1 Lenka
# 2 Martina
# 3 Gugu
Under the hood, enumerate()
yields a pair on each iteration. You can materialize it to inspect the structure:
seasons = ["Spring", "Summer", "Fall", "Winter"]
print(list(enumerate(seasons)))
# [(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
It’s also a regular iterator, so you can step through it with next()
:
it = enumerate(["a", "b", "c"], start=10)
print(next(it)) # (10, 'a')
print(next(it)) # (11, 'b')
print(next(it)) # (12, 'c')
How to use it (practical patterns)
1) Highlight the first item or a specific step
tasks = ["Pay Rent", "Clean Dishes", "Buy Milk"]
for idx, task in enumerate(tasks):
if idx == 0:
print(f"* {task.upper()}!")
else:
print(f"* {task}")
# * PAY RENT!
# * Clean Dishes
# * Buy Milk
2) Grab every second character from a string
secret = "3LAigf7eq 5fhiOnpdDs2 Ra6 nwUalyo.9"
message = []
for i, ch in enumerate(secret):
if i % 2: # odd indexes only
message.append(ch)
print("".join(message))
3) Add line numbers while scanning file content
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.")
4) Iterate dictionaries with an index
In modern Python, dicts preserve insertion order, so you can index the pairs consistently:
months = {
"january": "winter",
"february": "winter",
"march": "spring",
}
for idx, (month, season) in enumerate(months.items(), start=1):
print(f"{idx}: {month} - {season}")
# 1: january - winter
# 2: february - winter
# 3: march - spring
Limits and trade‑offs
- It’s an iterator, not a list. You can loop it once. If you need random access, cast to
list(...)
first. - It indexes a single iterable. If you’re pairing multiple sequences,
zip(...)
is a better fit. - Avoid
range(len(seq))
unless you truly need indexes only.enumerate()
communicates both intent and structure more clearly.
When not to use enumerate()
- Multi-sequence iteration: Prefer
zip(a, b)
and unpack both values directly. - Slicing tasks: If you only need a slice like “first 4 chars,” use slicing, not
enumerate()
plus conditionals. - Adjacent pairs: For routes or consecutive items, reach for tools like
itertools.pairwise
rather than manual indexing.
# zip() beats enumerate()+index reuse for multi-sequence loops
pets = ["Leo", "Aubrey", "Frieda"]
owners = ["Bartosz", "Sarah Jane", "Philipp"]
for pet, owner in zip(pets, owners):
print(f"{pet} & {owner}")
FAQs
What does enumerate()
return?
An enumerate object that yields (index, value)
tuples as you iterate.
How do I start counting from 1?
Pass start=1
to enumerate(iterable, start=1)
.
Can I reuse the same enumerate object?
Not after it’s been consumed. It’s an iterator. Create a new one or materialize it as a list if you need multiple passes.
Is enumerate()
faster than manual counters?
It’s implemented efficiently and removes bookkeeping from your code. Beyond performance, its clarity is the main win.
Comments