enumerate
corebeginnerenumerate(iterable) wraps any iterable in a lazy sequence of (index, item) pairs, starting at 0 by default. It replaces the manual counter loop with one built into the language.
Think of it as
A numbered coat-check ticket handed out as each coat arrives, rather than the coat-checker keeping a separate tally sheet. The number and the item travel together as one pair from the moment enumerate hands them out — there is nothing to keep in sync by hand.
What we're doing: Loop with enumerate at the default start, then at start=1, and compare against the manual counter it replaces.
- 3
- enumerate pairs each letter with its 0-based position — no separate counter variable to declare or update.
- 6
- start=1 shifts every index by one; the items and their relative order are unchanged.
- 10
- The equivalent manual loop: a counter initialized before the loop and incremented by hand on every pass — exactly what enumerate exists to remove.
0 a
1 b
2 c
1 a
2 b
3 c
0 a
1 b
2 cWhy this works: enumerate wraps an iterable in a second, lazy iterable that yields (index, item) tuples — unpacking each pair directly in the for statement (for i, letter in ...) is what makes the index available without a separate variable. Because it is lazy, enumerate never builds the whole list of pairs up front; it produces the next one only when the loop asks. start shifts where counting begins but never changes which item pairs with which position relative to the others — it only reshapes the numbers, not the order.
Indexing back into the sequence instead of using the paired item
Wrong
Better
What you see: IndexError: list index out of range on the last iteration — letters[i + 1] reaches one past the end of the list.
Why: enumerate already hands over the item at position i as letter; indexing back into the original sequence with letters[i + 1] is not just redundant, it silently asks for the WRONG item — one ahead of the pair enumerate actually produced — and eventually walks off the end. The item enumerate yields is the one to use directly; there is no reason to re-derive it from the index.
- an iterable — ["a", "b", "c"]
- enumerate() pairs each item — with a running index, lazily
- (index, item) tuples — (0, 'a'), (1, 'b'), (2, 'c')
enumerate over ["a", "b", "c"]
Together

