What Does Enumerate Do in Python? Examples and Uses

Python’s enumerate() function gives a loop both the current value and a counter. That means you can write for index, value in enumerate(items): instead of creating and updating an index variable by hand. Unless you choose another starting number, the counter begins at 0.

Reach for enumerate() when the position has a real job: numbering output, identifying a row, reporting where validation failed or updating a second position-based structure. If you are still deciding where loops fit into real projects, our guide to what Python is used for surveys the language’s broader applications. When you never use the position, an ordinary for loop remains the clearer option.

What enumerate does in Python, including counters, start values, iterables, lazy iteration, and cleaner loops
What does enumerate do in Python? It pairs each iterable value with a counter and yields the pairs lazily.

What does enumerate do in Python?

enumerate(iterable, start=0) returns an enumerate object. Ask that object for its next value and you get a two-item tuple: the current count, followed by the next item from the original iterable. The official Python documentation for enumerate() says the input may be a sequence, an iterator or any other object that supports iteration.

fruits = ["apple", "pear", "orange"]
for index, fruit in enumerate(fruits):
    print(index, fruit)

The output is:

0 apple
1 pear
2 orange

Each trip through the loop unpacks the pair. The first part goes into index; the second goes into fruit. Those variable names are not special—position and item would behave in exactly the same way.

Diagram of one Python enumerate loop step combining a counter and iterable value, then unpacking the tuple
One enumerate step combines the counter and current value into a tuple that the loop can unpack.

How to change the starting number

Use the optional start argument when the visible numbering should begin somewhere other than zero. A numbered menu or human-readable list, for example, will usually begin at one:

tasks = ["Back up files", "Install update", "Restart service"]
for number, task in enumerate(tasks, start=1):
    print(f"{number}. {task}")

This prints:

1. Back up files
2. Install update
3. Restart service

Here is the detail that often catches people: the starting value changes only the counter that enumerate() returns. It does not slice or reorder the list, nor does it make Python’s actual list indexes begin at one. On the first iteration above, number is 1, while the task still comes from tasks[0].

Python enumerate start argument comparison showing counters 0, 1, 2 and 1, 2, 3 while list indexes remain unchanged
The start argument changes enumerate’s counter, but it does not change the underlying list indexes.

Why use enumerate instead of range and len?

A loop based on range(len(items)) can retrieve an index, but it makes the code perform a separate lookup to obtain each value:

for index in range(len(fruits)):
    fruit = fruits[index]
    print(index, fruit)

With enumerate(), the value arrives directly from the iterable:

for index, fruit in enumerate(fruits):
    print(index, fruit)

The second version states what the loop needs without making the reader work through the indexing machinery. It also works with generators and other iterables that have neither len() nor integer indexes. Save range() for a loop that truly needs a sequence of numbers, rather than positions attached to values that already exist.

Enumerate is lazy: it returns an iterator

Calling enumerate() does not build a list containing every possible pair. It gives you an iterator, which produces each pair only when requested. That lazy behavior matters when the input is large or generates its values on demand.

result = enumerate(["a", "b", "c"], start=10)
print(next(result))
print(next(result))

The two calls return (10, "a") and (11, "b"). Like any iterator, this enumerate object is consumed as it moves forward. You can turn all of its pairs into a list with list(enumerate(items)), which can be handy while debugging. Avoid doing that automatically, though; a normal loop can consume the pairs directly.

Python enumerate lazy iterator producing the pairs (10, a), (11, b), and (12, c) one at a time
An enumerate object advances lazily, producing one counter-value pair whenever the iterator is requested.

What can you enumerate?

Any iterable is fair game: lists, tuples, strings, generators, open files and dictionary views all work. The value beside the counter is simply whatever an ordinary loop over that object would have produced.

for position, letter in enumerate("cat", start=1):
    print(position, letter)

Iterating over a dictionary produces keys, so enumerating a dictionary pairs a count with each key. To receive both dictionary keys and values, enumerate its item view:

scores = {"Ada": 92, "Grace": 97}
for row, (name, score) in enumerate(scores.items(), start=1):
    print(row, name, score)

This loop performs two levels of unpacking. First, enumerate() supplies row and the current dictionary item. Then (name, score) opens that item into its key and value. The parentheses are optional, but they make the shape much easier to spot.

Python enumerate supported iterables including lists, strings, tuples, generators, and dictionary items
Enumerate works with any iterable and pairs its normal loop values with a counter.

Practical uses for enumerate

Display numbered choices

options = ["Save", "Export", "Quit"]
for number, option in enumerate(options, start=1):
    print(f"{number}: {option}")

The user sees a number beside each choice, and the code never has to increment a separate counter.

Report where validation failed

temperatures = [18.4, 19.1, None, 20.0]
for row_number, temperature in enumerate(temperatures, start=1):
    if temperature is None:
        print(f"Missing value on row {row_number}")

Starting at one is useful here because the error number now matches a spreadsheet or text file whose first visible row is labeled one.

Compare an item with its neighbour

readings = [12, 15, 14, 20]
for index, reading in enumerate(readings[1:], start=1):
    previous = readings[index - 1]
    print(reading - previous)

In this example, the counter deliberately lines up with the original list. That is a legitimate index-based job. If the index itself is only an incidental detail, zip(readings, readings[1:]) may express the comparison more clearly.

Common mistakes with enumerate

  • Keeping a manual counter as well: the counter returned by enumerate() already advances once per value.
  • Treating the counter as a changed list index: a custom start affects only the returned count.
  • Forgetting to unpack the pair: one loop variable receives a tuple such as (0, "apple").
  • Using the index when it serves no purpose: loop directly over the values when you do not need positions.
  • Reusing an exhausted enumerate object: create a new one if the same iterable must be traversed again.
Python loop decision guide for choosing enumerate, a plain for loop, range, or zip
Choose enumerate when you need each value and its position; use simpler loop tools for other jobs.

How enumerate works conceptually

The language documentation describes behavior equivalent to keeping a count, yielding it beside each element, and then incrementing it. Seen this way, both important details make sense: the result is lazy, and its initial count can be any integer.

def simple_enumerate(iterable, start=0):
    count = start
    for element in iterable:
        yield count, element
        count += 1

Use the built-in in real code; this helper is only a window into the mechanics. The proposal that introduced the feature, PEP 279, describes its goal as a clean, memorable way to pair values from an iterable with a count.

Frequently asked questions

Does enumerate change the original iterable?

No. It reads each value and pairs it with a counter. The source is not copied or modified, and no indexes are added to it.

Does enumerate always start at zero?

Zero is only the default. Pass another integer through the optional start argument, as in enumerate(items, start=1), to begin elsewhere.

What type does enumerate return?

It returns an enumerate object, which is an iterator. Each step from that iterator produces a tuple containing the count and one input value.

Can enumerate work with a generator?

Yes. Because a generator is iterable, enumerate() can attach a counter as each value appears. There is no need to convert the generator to a list first.

Key takeaway

enumerate() pairs each value from an iterable with a counter and yields those pairs one at a time. Use it when a loop needs both position and value, set start when displayed numbering should begin elsewhere, and use a plain loop when the position is irrelevant.