087. Enumerate()
Access both index and value when iterating without manual counters
087. Enumerate()
He generates a ranked process report. The top process is #1, not #0. He uses a manual counter:
i = 0
for proc in sorted_processes:
print(f"{i}. {proc['name']}: {proc['rss_mb']} MB")
i += 1A senior dev flags the PR: “Use enumerate.”
for rank, proc in enumerate(sorted_processes, start=1):
print(f"{rank}. {proc['name']}: {proc['rss_mb']} MB")Same result. No manual counter. No off-by-one bugs. He also uses enumerate when he needs to find which position in the list a process occupies — without keeping a separate index variable.
def find_rank(processes, name):
for i, proc in enumerate(processes):
if proc["name"] == name:
return i # 0-based rank
return -1💡 Fun fact: enumerate() is implemented in C inside CPython and is highly optimized — it produces (index, value) tuples lazily without building any intermediate list. The start parameter was added in Python 2.6 after widespread demand from developers who kept writing enumerate(items, 1) workarounds.
⚠️ Watch out: The most common enumerate mistake is forgetting to unpack the tuple — writing for item in enumerate(items) instead of for i, item in enumerate(items). This gives you tuples like (0, "Pizza") instead of a clean index and value, and downstream code silently operates on the wrong type.
🤔 Think about it: enumerate(items, start=1) gives 1-based numbering, but the underlying list is still 0-indexed. If you use the enumerated index to access items[i], you’ll get an off-by-one error. How would you safely use the index from enumerate(items, start=1) to look up elements?
Learning objectives
- Use enumerate() to get index and value simultaneously
- Set a custom starting index with the start parameter
- Replace manual counter variables with enumerate
Key concepts
- enumerate
- index-value pairs
- start parameter
Try it
Concept detail
enumerate(iterable) wraps an iterable and yields (index, value) tuples.
for i, item in enumerate(["a", "b", "c"]):
print(i, item)
# 0 a
# 1 b
# 2 cStart at a custom index: for i, item in enumerate([“a”, “b”, “c”], start=1): print(i, item) # 1 a # 2 b # 3 c
WHY enumerate instead of a manual counter: # Manual — error-prone, easy to forget to increment: i = 0 for item in items: print(i, item) i += 1 # easy to forget
# enumerate — no counter to manage:
for i, item in enumerate(items):
print(i, item)Materialize as list: list(enumerate([“a”, “b”])) # → [(0, ‘a’), (1, ‘b’)]
Combine with list comprehension: [f“{i}: {v}“ for i, v in enumerate(items, start=1)]
enumerate is lazy — it generates pairs one at a time, no list built in memory.
Solution
def numbered_menu(items):
return [f"{i}. {item}" for i, item in enumerate(items, start=1)]
def find_index(items, target):
for i, item in enumerate(items):
if item == target:
return i
return -1Tests
def test_numbered_menu_basic():
result = numbered_menu(["Pizza", "Pasta", "Salad"])
assert result[0] == "1. Pizza"
assert result[1] == "2. Pasta"
assert result[2] == "3. Salad"
def test_numbered_menu_starts_at_1():
result = numbered_menu(["A"])
assert result[0].startswith("1.")
def test_numbered_menu_single():
assert numbered_menu(["Only"]) == ["1. Only"]
def test_numbered_menu_empty():
assert numbered_menu([]) == []
def test_find_index_found():
assert find_index(["a", "b", "c"], "b") == 1
def test_find_index_first():
assert find_index(["x", "y", "z"], "x") == 0
def test_find_index_last():
assert find_index(["a", "b", "c"], "c") == 2
def test_find_index_not_found():
assert find_index(["a", "b"], "z") == -1