Python - enumerate() Function with Examples

When you iterate over a sequence in Python, you often need both the element and its position. Before discovering `enumerate()`, many developers write code like this:

Key Insights

  • enumerate() returns an iterator of tuples containing both index and value, eliminating the need for manual counter variables or the range(len()) anti-pattern
  • The optional start parameter lets you begin counting from any integer, which is essential for user-facing numbered lists that should start at 1
  • enumerate() is memory-efficient because it yields items lazily rather than creating a full list in memory, making it suitable for large iterables and file processing

What enumerate() Does and Why You Need It

When you iterate over a sequence in Python, you often need both the element and its position. Before discovering enumerate(), many developers write code like this:

colors = ['red', 'green', 'blue']
index = 0
for color in colors:
    print(f"{index}: {color}")
    index += 1

This works, but it’s verbose and error-prone. You might forget to increment the counter, or accidentally increment it in the wrong place. The enumerate() function solves this problem elegantly by returning an iterator that yields tuples of (index, element) pairs.

enumerate() is a built-in function—no imports required. It’s one of those Python features that, once you learn it, you’ll use constantly.

Basic Syntax and Parameters

The function signature is straightforward:

enumerate(iterable, start=0)

The iterable parameter accepts any iterable object: lists, tuples, strings, dictionaries, generators, file objects, and more. The start parameter specifies where the index counting begins, defaulting to 0.

Here’s the basic usage pattern with tuple unpacking:

fruits = ['apple', 'banana', 'cherry', 'date']

for index, fruit in fruits:
    print(f"Index {index}: {fruit}")

Wait—that code has a bug. You can’t unpack a string directly. Here’s the correct version:

fruits = ['apple', 'banana', 'cherry', 'date']

for index, fruit in enumerate(fruits):
    print(f"Index {index}: {fruit}")

Output:

Index 0: apple
Index 1: banana
Index 2: cherry
Index 3: date

When displaying numbered lists to users, starting at 0 looks odd. Use the start parameter:

fruits = ['apple', 'banana', 'cherry', 'date']

for number, fruit in enumerate(fruits, start=1):
    print(f"{number}. {fruit}")

Output:

1. apple
2. banana
3. cherry
4. date

The start parameter accepts any integer, including negative values:

for i, item in enumerate(['a', 'b', 'c'], start=-1):
    print(f"{i}: {item}")

Output:

-1: a
0: b
1: c

Common Use Cases

Displaying a Numbered Menu

This is perhaps the most common practical application. When building CLI tools or text-based interfaces, you need to present options with numbers:

def display_menu(options):
    print("Please select an option:")
    for number, option in enumerate(options, start=1):
        print(f"  {number}) {option}")
    print()
    
menu_items = ['View profile', 'Edit settings', 'Check notifications', 'Logout']
display_menu(menu_items)

# User input handling
choice = int(input("Enter your choice: "))
selected = menu_items[choice - 1]  # Adjust for 0-based indexing
print(f"You selected: {selected}")

Replacing the range(len()) Anti-Pattern

If you’ve written Python for any length of time, you’ve probably written this:

# Don't do this
items = ['first', 'second', 'third']
for i in range(len(items)):
    print(f"{i}: {items[i]}")

This pattern has several problems. It’s harder to read, requires indexing into the list manually, and creates opportunities for off-by-one errors. Here’s the improved version:

# Do this instead
items = ['first', 'second', 'third']
for i, item in enumerate(items):
    print(f"{i}: {item}")

The enumerate() version is more readable, more Pythonic, and less error-prone. The intent is immediately clear: you want both the index and the item.

Finding Element Positions

When you need to locate items that match certain criteria and record their positions:

temperatures = [72, 75, 79, 85, 91, 88, 82, 77]
threshold = 85

hot_days = []
for day, temp in enumerate(temperatures):
    if temp >= threshold:
        hot_days.append(day)

print(f"Days with temperature >= {threshold}°F: {hot_days}")
# Output: Days with temperature >= 85°F: [3, 4, 5]

Working with Different Iterables

Strings

enumerate() works seamlessly with strings, treating each character as an element:

word = "Python"
for position, char in enumerate(word):
    print(f"Position {position}: '{char}'")

Output:

Position 0: 'P'
Position 1: 'y'
Position 2: 't'
Position 3: 'h'
Position 4: 'o'
Position 5: 'n'

This is useful for text processing tasks like finding character positions or building character-position mappings.

File Objects

Processing files with line numbers is a common requirement for log analysis, error reporting, and data validation:

def validate_config(filepath):
    errors = []
    with open(filepath, 'r') as file:
        for line_num, line in enumerate(file, start=1):
            line = line.strip()
            if not line or line.startswith('#'):
                continue
            if '=' not in line:
                errors.append(f"Line {line_num}: Missing '=' in '{line}'")
    return errors

# Example usage with a config file
# errors = validate_config('app.conf')
# for error in errors:
#     print(error)

The start=1 is crucial here because users expect line numbers to begin at 1, not 0.

Dictionaries

When you enumerate a dictionary, you iterate over its keys by default:

config = {'host': 'localhost', 'port': 8080, 'debug': True}

for i, key in enumerate(config):
    print(f"{i}: {key} = {config[key]}")

For key-value pairs with indices, combine enumerate() with .items():

config = {'host': 'localhost', 'port': 8080, 'debug': True}

for i, (key, value) in enumerate(config.items()):
    print(f"{i}: {key} = {value}")

Advanced Patterns

List Comprehensions with Index Filtering

enumerate() integrates naturally with list comprehensions when you need index-based filtering:

# Get elements at even indices
data = ['a', 'b', 'c', 'd', 'e', 'f']
even_indexed = [item for i, item in enumerate(data) if i % 2 == 0]
print(even_indexed)  # ['a', 'c', 'e']

# Transform elements based on position
transformed = [item.upper() if i < 2 else item for i, item in enumerate(data)]
print(transformed)  # ['A', 'B', 'c', 'd', 'e', 'f']

Modifying List Elements In Place

When you need to modify a list based on index conditions:

scores = [85, 92, 78, 95, 88, 72]

# Add 5 bonus points to scores below 80
for i, score in enumerate(scores):
    if score < 80:
        scores[i] = score + 5

print(scores)  # [85, 92, 83, 95, 88, 77]

Combining with zip()

For parallel iteration with indices across multiple sequences:

names = ['Alice', 'Bob', 'Charlie']
scores = [95, 87, 92]

for rank, (name, score) in enumerate(zip(names, scores), start=1):
    print(f"Rank {rank}: {name} - {score} points")

Output:

Rank 1: Alice - 95 points
Rank 2: Bob - 87 points
Rank 3: Charlie - 92 points

Performance Considerations

enumerate() returns an iterator, not a list. This means it generates index-value pairs on demand rather than computing them all upfront. For large iterables, this lazy evaluation is memory-efficient:

# This doesn't load the entire file into memory
with open('large_file.txt') as f:
    for line_num, line in enumerate(f, start=1):
        if 'ERROR' in line:
            print(f"Error found at line {line_num}")
            break  # Stop early without processing remaining lines

If you actually need a list of tuples, you can convert explicitly:

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

But in most cases, iterating directly is what you want.

Summary

Use enumerate() whenever you need both the index and value during iteration. It’s cleaner than maintaining a manual counter and safer than the range(len()) pattern.

Key practices to remember:

  • Use start=1 when displaying numbered lists to users
  • Prefer enumerate() over range(len(sequence)) for cleaner, more readable code
  • Combine with tuple unpacking in for loops for the most readable syntax
  • Remember it works with any iterable, not just lists
  • Take advantage of its lazy evaluation for memory-efficient processing of large files or generators

The function is simple, but mastering it will make your Python code more idiomatic and maintainable.

Liked this? There's more.

Every week: one practical technique, explained simply, with code you can use immediately.