What it is

Python’s enumerate() wraps any iterable and yields pairs of (index, value). By default the index starts at 0, but you can choose a custom start value.

Why it matters

If you’ve ever written a loop with a manual counter or range(len(...)), you’ve added noise and opportunities for off-by-one errors. enumerate() gives you the index and the item in one pass—clearer code, fewer bugs.

How it works / How to use

Basic loop

letters = ["a", "b", "c"]

for i, ch in enumerate(letters):
    print(i, ch)
# 0 a
# 1 b
# 2 c

Start counting at 1

for i, ch in enumerate(letters, start=1):
    print(i, ch)
# 1 a
# 2 b
# 3 c

It’s an iterator (lazy), not a list

pairs = enumerate(letters)
print(pairs)             # <enumerate object at 0x...>

print(list(pairs))       # [(0, 'a'), (1, 'b'), (2, 'c')]

Peek under the hood with next()

pairs = enumerate(["x", "y"], start=10)
print(next(pairs))  # (10, 'x')
print(next(pairs))  # (11, 'y')
# next(pairs) would raise StopIteration

The values arrive one at a time because enumerate() returns an iterator. You can advance it with the built-in next().

Unpacking with dictionaries

months = {"jan": 31, "feb": 28, "mar": 31}

for idx, (name, days) in enumerate(months.items(), start=1):
    print(f"{idx}. {name} = {days}")
# 1. jan = 31
# 2. feb = 28
# 3. mar = 31

Use dict.items() to get key–value pairs, then unpack alongside the index from enumerate().

Common patterns

  • Skip by index: if i % 2: continue
  • Label output: print(f"{i+1}) {value}") when you want 1-based display
  • Track line numbers while reading files

Limits / Trade‑offs

  • Iterator semantics: Once you consume an enumerate object, it’s exhausted. Convert to a list first if you need to reuse it.
  • Not a replacement for every index need: If you’re iterating multiple sequences in lockstep, zip() is usually a better fit.
  • Order comes from the iterable: For mappings, the order reflects the dictionary’s insertion order (Python 3.7+ guarantee).

FAQs

How do I start counting from 1?
Pass start=1: enumerate(items, start=1).

Is enumerate() more efficient than range(len(...))?
Yes for readability, and typically equivalent or better for performance because it doesn’t index into the sequence each loop. It also works with any iterable, not just sequences with len.

Can I get only the indexes?
If you only need positions, use range(len(seq)) for sequences. Otherwise, loop with enumerate() and ignore the value: for i, _ in enumerate(iterable): ...

What exactly does it return?
An enumerate object that yields (index, value) tuples on iteration. You can materialize it with list(enumerate(...)) if needed.