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.
Member discussion