When and how do you use enumerate() and zip() in Python, and what are common mistakes when using them together?
enumerate() pairs each element with its index without maintaining a manual counter. zip() pairs elements from multiple iterables together. Both return lazy iterators, so they compose efficiently. The key trap with zip() is silent truncation when iterables differ in length.
How to think about it
What this question is really testing
The interviewer wants to see that you reach for built-ins like enumerate and zip instead of manually tracking index variables. Both functions are lazy iterators — they produce values on demand without building a full list in memory. That composability is the point.
enumerate — index + value without a counter variable
The classic mistake is writing i = 0; ... i += 1. enumerate eliminates that entirely by yielding (index, value) tuples:
fruits = ["apple", "banana", "cherry"]
for i, fruit in enumerate(fruits):
print(i, fruit)
# 0 apple
# 1 banana
# 2 cherry
# start= lets you begin from any number
for i, fruit in enumerate(fruits, start=1):
print(i, fruit) # 1 apple, 2 banana, 3 cherry
zip — walk two iterables in lockstep
zip is the cleanest way to pair up related lists or to build a dict from two sequences:
names = ["Alice", "Bob", "Carol"]
scores = [88, 92, 79]
for name, score in zip(names, scores):
print(f"{name}: {score}")
# One-liner dict from two lists
mapping = dict(zip(names, scores))
# {'Alice': 88, 'Bob': 92, 'Carol': 79}
Combining enumerate and zip
When you need both a position index and paired values, nest them — zip first, then enumerate:
for i, (name, score) in enumerate(zip(names, scores)):
print(i, name, score)
zip_longest when lengths may differ
zip stops silently at the shortest iterable. Use zip_longest when you need all elements:
from itertools import zip_longest
for a, b in zip_longest([1, 2, 3], [10, 20], fillvalue=0):
print(a, b)
# 1 10
# 2 20
# 3 0 <- filled
Try it yourself
The key insight
Both enumerate and zip are lazy — they yield tuples on demand, adding O(1) memory overhead regardless of input size. Chaining them composes a pipeline that never builds an intermediate list.