Back to Blog
Python

Python enumerate Function: Syntax and Examples

Learn how to use Python's enumerate() to get both index and value in loops, with practical examples and performance notes.

enumerateiterationloopsindexingiterators
Illustration of a Python loop showing an index counter paired with a value, representing the enumerate function's dual output.

Python's enumerate() built-in lets you iterate over an iterable and also know the position of each item. Instead of maintaining a separate counter or using range(len(sequence)), enumerate returns an iterator that yields index-value pairs. It works with any iterable, not just lists.

What Does enumerate Do?

enumerate takes an iterable and returns an enumerate object, which is itself an iterator. Each time you ask for the next item, it produces a tuple containing the current count (starting at 0 by default) and the next element from the original iterable. This removes the need to maintain a separate counter variable, reducing the chance of off-by-one errors and making the code more readable.

For example, consider a simple list of strings:

colors = ['red', 'green', 'blue'] for index, color in enumerate(colors): print(index, color)

This prints:

0 red
1 green
2 blue

The loop unpacks each tuple directly into index and color. The enumerate object yields tuples lazily, so it does not create a list of tuples in memory; it generates each pair on demand.

Basic Syntax and Return Behavior

The signature of enumerate is enumerate(iterable, start=0). The start parameter controls the initial index value. If you omit it, the first index is 0. If you pass a different value, the counter begins there.

letters = ['a', 'b', 'c'] for i, letter in enumerate(letters, start=1): print(i, letter)

Output:

1 a
2 b
3 c

The enumerate object is an iterator, which means it is consumed once. If you need to reuse it, you must create a new one or convert it to a list first. Converting to a list gives you a list of tuples, which may be useful for debugging or when you need random access.

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

Using enumerate with Different Iterables

enumerate works with any iterable, including tuples, strings, sets, dictionaries, and file objects. When used with a dictionary, it iterates over the keys by default. If you need both the key and the value, you can call .items() on the dictionary first.

person = {'name': 'Alice', 'age': 30} for index, key in enumerate(person): print(index, key)

Output:

0 name
1 age

To get the key-value pairs with an index:

for index, (key, value) in enumerate(person.items()): print(index, key, value)

This prints:

0 name Alice
1 age 30

Strings are iterable character by character, so you can enumerate over them as well. This is useful when you need to track character positions.

Starting the Index at a Custom Value

The start argument is useful when the index should reflect a position in a larger context, such as a line number in a file or a row number in a report. For example, when reading a file, you might want line numbers starting at 1:

with open('data.txt') as f: for line_number, line in enumerate(f, start=1): print(line_number, line.strip())

This pattern avoids manually incrementing a counter and keeps the code concise. The start value does not affect the iterable itself; it only changes the first number produced.

Unpacking enumerate Results in Loops

A common pattern is to unpack the tuple directly in the for statement. This works because enumerate yields two-element tuples. You can also use it in comprehensions, where you need both the index and the value to build a new collection.

squares = [i * i for i, value in enumerate([10, 20, 30])] print(squares) # [0, 1, 4]

Here, i is the index and value is the original element. The comprehension uses the index to compute a new value. This is a compact way to transform a sequence while having access to positions.

Another useful pattern is to use enumerate to find the index of a specific element without calling list.index(), which raises an exception if the element is missing. Instead, you can loop and break when the condition is met:

def find_index(sequence, target): for i, item in enumerate(sequence): if item == target: return i return -1

This approach is explicit and avoids the overhead of exception handling for missing items.

Performance and Memory Considerations

enumerate does not create a new list of tuples; it generates pairs lazily, so the memory overhead stays constant regardless of the size of the iterable. It also avoids a separate indexing step that range(len(seq)) plus seq[i] requires. For most applications, any performance difference is not important, and enumerate is the more idiomatic choice.

When you convert enumerate to a list with list(enumerate(seq)), you do create a full list of tuples. This is useful when you need random access or want to store the pairs, but it consumes memory proportional to the sequence length. For large sequences, prefer iterating directly over the enumerate object to avoid that memory allocation.

Another subtle point: enumerate works with infinite iterators. Because it is lazy, you can use it with itertools.count or a generator that produces values indefinitely. The index will continue incrementing without bound, which is fine as long as you break out of the loop at some point.

Common Mistakes and Edge Cases

One common mistake is to forget that enumerate returns an iterator, not a list. If you try to access it by index or call len() on it, you will get a TypeError. Always convert to a list if you need those operations.

Another edge case is using enumerate on a set or dictionary. Sets are unordered, and dictionary iteration order follows insertion order in modern Python. Either way, the index values are assigned from the iteration order, so relying on them as meaningful positions can be confusing. If you need a stable order, sort the items first or use a list.

When you unpack the tuple, be careful with the number of variables. If the iterable yields elements that are themselves tuples, you may need to nest the unpacking parentheses, as shown earlier with person.items(). Without those parentheses, Python sees the outer tuple from enumerate as having only two items and raises a ValueError during unpacking.

Finally, remember that the start parameter is an integer. Passing a non-integer value, such as a float, will raise a TypeError. This is a minor but frequent mistake when someone tries to start the index at 1.0.

Using enumerate with Reversed Sequences

Sometimes you need to iterate over a reversed sequence while keeping track of the original index. enumerate combined with reversed can achieve this. For example, to iterate from the end while still showing the original position:

items = ['a', 'b', 'c', 'd'] for index, item in enumerate(reversed(items)): print(len(items) - 1 - index, item)

This prints:

3 d
2 c
1 b
0 a

The expression len(items) - 1 - index converts the reversed position back to the original index. This pattern is useful when you need to process a list in reverse but still reference the original positions, for example when modifying a list while iterating backwards.

Another approach is to use enumerate on a slice that reverses the list, but that creates a copy. Using reversed is more memory-efficient because it returns an iterator. This technique is particularly relevant when working with large sequences where copying would be expensive.

Python enumerate Function: Syntax, Examples, and Performance Notes | RYUSLOG DEV