Python enumerate() and zip()
1Python enumerate & zip: overview
enumerate() gives you each item together with its position, and zip() walks two or more sequences side by side, so one for loop can handle matching items without any index juggling.
Before this lesson: Python for Loops, Python range() Function
2Python enumerate & zip: diagram
3Python enumerate & zip: explained step by step
A plain for loop hands you the items of a collection, but it doesn't tell you where an item came from, and it can only walk one collection at a time. Two built-in helpers fix that: enumerate() adds the position, and zip() walks several collections in step. (A list is just an ordered collection written in square brackets, like ["Asha", "Ben"]. That is all you need to know about lists here.)
enumerate(): the item and its position
enumerate(items) produces pairs of (index, item): first (0, first item), then (1, second item), and so on.
names = ["Asha", "Ben", "Chen"]
for index, name in enumerate(names):
print(index, name)The loop header has two variable names. Each pair coming out of enumerate is unpacked into them by position: the first value goes into index, the second into name. With only one variable, the loop would hold the whole pair instead:
for pair in enumerate(["Asha", "Ben"]):
print(pair)Counting from 1 with start
Indexes begin at 0, but people count from 1. Pass start=1 and the numbering begins there. Only the numbers change: you still get every item, and none are skipped.
names = ["Asha", "Ben", "Chen"]
for number, name in enumerate(names, start=1):
print(f"{number}. {name}")Why not range(len(items))?
You can get the same result with the older style:
for i in range(len(names)):
print(i, names[i])
It works, but you must build a range, ask for the length, and look every item up by index yourself. That is three chances to slip: range(1, len(names)) silently skips the first item, and names[i + 1] crashes on the last one. enumerate says what you mean ("each item with its position") in one step, and it also works on things you can loop over but cannot index, such as the lines of a file.
zip(): walk through lists in step
Sometimes two lists belong together: names and their scores, products and their prices. zip(names, scores) pairs the first name with the first score, the second with the second, and so on, like the two sides of a zip fastener meeting.
names = ["Asha", "Ben", "Chen"]
scores = [88, 92, 79]
for name, score in zip(names, scores):
print(name, "scored", score)You are not limited to two. Give zip three lists and every item is a triple:
names = ["Asha", "Ben", "Chen"]
scores = [88, 92, 79]
grades = ["B", "A", "C"]
for name, score, grade in zip(names, scores, grades):
print(name, score, grade)zip stops at the shortest
If the lists have different lengths, zip stops as soon as the shortest one runs out. The leftover items are dropped without any warning.
days = ["Mon", "Tue", "Wed", "Thu"]
sales = [120, 95, 140]
for day, amount in zip(days, sales):
print(day, amount)
print("Pairs made:", len(list(zip(days, sales))))Thu has no sales figure, so it simply vanishes. If a length mismatch would mean your data is broken, add strict=True (Python 3.10 and newer). Then a mismatch raises a ValueError instead of quietly losing data:
for day, amount in zip(days, sales, strict=True):
print(day, amount)
# Mon, Tue and Wed are printed, then Python stops with:
# ValueError: zip() argument 2 is shorter than argument 1
Using both together
To number the pairs that zip produces, wrap it in enumerate. Each item is now (number, (name, score)), so the inner pair gets its own parentheses when you unpack it:
names = ["Asha", "Ben", "Chen"]
scores = [88, 92, 79]
for number, (name, score) in enumerate(zip(names, scores), start=1):
print(f"{number}. {name} - {score}")Building a dictionary from two lists
A dictionary stores key-value pairs, and zip(keys, values) produces exactly such pairs, so dict() can swallow it in one go:
keys = ["name", "age", "city"]
values = ["Asha", 21, "Pune"]
profile = dict(zip(keys, values))
print(profile)
print(profile["city"])Unzipping with zip(*pairs)
The same tool works in reverse. The * spreads a list of pairs into separate arguments, and zip then regroups all the first items together and all the second items together:
pairs = [("Asha", 88), ("Ben", 92), ("Chen", 79)]
names, scores = zip(*pairs)
print(names)
print(scores)The results are tuples (read-only lists written with parentheses).
They are lazy: one pass only
Neither enumerate() nor zip() builds its pairs up front. They return an iterator, a small object that makes the next pair only when something asks for it. If you write print(enumerate(names)) you will see text like <enumerate object at 0x7f3c...> (the address changes from run to run) instead of the pairs. A for loop asks for one pair at a time, so loops work as usual. To look at all the pairs at once, wrap the iterator in list().
The catch: an iterator can be used once. After a loop, or a list(), has taken all of its pairs, it is empty.
names = ["Asha", "Ben"]
pairs = enumerate(names, start=1)
print(list(pairs))
print(list(pairs))The second line shows an empty list: the first list(pairs) already used everything up. zip behaves the same way. If you need the pairs again, build a fresh zip(...) or keep them with pairs = list(zip(...)).
Quick reference: Python enumerate & zip
- How to Convert a List to a Dictionary in Python — Use `dict(zip(keys, values))` to build a dictionary from two lists, or `dict(list_of_pairs)` when each item i…
- Python Program to Transpose a Matrix — To transpose a matrix stored as a list of lists, use `[list(row) for row in zip(*matrix)]`: rows become colum…
- Python enumerate() Function — `enumerate(iterable, start=0)` wraps an iterable so a `for` loop receives `(index, item)` pairs, counting fro…
- Python zip() Function — `zip(*iterables, strict=False)` combines items from several iterables into tuples, stopping when the shortest…
4Python enumerate & zip: syntax
enumerate(iterable) # (0, item), (1, item), ...
enumerate(iterable, start=1) # (1, item), (2, item), ...
for index, item in enumerate(items):
...
zip(a, b) # (a0, b0), (a1, b1), ... stops at the shortest
zip(a, b, c) # three items at a time
zip(a, b, strict=True) # ValueError if the lengths differ (3.10+)
for x, y in zip(a, b):
...
dict(zip(keys, values)) # build a dictionary
firsts, seconds = zip(*pairs) # unzip a list of pairs
list(enumerate(items)) # see all the pairs at once
5Python enumerate & zip: run the example
Press Run (or Ctrl + Enter). The output (step 6) appears directly below the code. Change anything and run it again.
students = ["Asha", "Ben", "Chen", "Dia"]
scores = [88, 92, 79, 95]
for number, name in enumerate(students, start=1):
print(f"{number}. {name}")
for name, score in zip(students, scores):
if score >= 90:
print(name, "earned an A with", score)
report = dict(zip(students, scores))
print(report)Expected output1. Asha
2. Ben
3. Chen
4. Dia
Ben earned an A with 92
Dia earned an A with 95
{'Asha': 88, 'Ben': 92, 'Chen': 79, 'Dia': 95}
7Python enumerate & zip: how the example works
Hover or tap a step to highlight its lines in the example above.
- Line 1-2Two lists of the same length. The item at position 0 of
studentsbelongs with the item at position 0 ofscores, and so on. - Line 4-5
enumerate(students, start=1)produces(1, "Asha"),(2, "Ben"), ... Each pair is unpacked intonumberandname, which gives a numbered list. - Line 7
zip(students, scores)pairs each student with the score at the same position:("Asha", 88),("Ben", 92), ... Each pair is unpacked intonameandscore. - Line 8-9Only scores of 90 or more get a message, so just Ben and Dia are printed. No index was needed to match a name with its score.
- Line 11A fresh
zip(the one in the loop above is already used up) feeds its pairs todict(), which turns each name into a key and each score into its value. - Line 12The dictionary is printed in the order the pairs were added.
8Python enumerate & zip: common mistakes
Swapping the names in the unpacking
✗ Wrong
names = ["Asha", "Ben"]
for name, index in enumerate(names):
print(f"{name} is at {index}")✓ Right
names = ["Asha", "Ben"]
for index, name in enumerate(names):
print(f"{name} is at {index}")enumerate always puts the index first and the item second, and unpacking assigns by position, not by name. So name received the number. Write for index, name in enumerate(names).
Forgetting to unpack the pair
✗ Wrong
names = ["Asha", "Ben"]
for item in enumerate(names):
print(item.upper())✓ Right
names = ["Asha", "Ben"]
for index, name in enumerate(names):
print(name.upper())With a single loop variable, item receives the whole (index, name) pair, which is a tuple and not a string. Unpack the pair in the for line, or pick the name with item[1].
Using a zip twice
✗ Wrong
names = ["Asha", "Ben"]
scores = [88, 92]
pairs = zip(names, scores)
print(list(pairs))
for name, score in pairs:
print(name, score)✓ Right
names = ["Asha", "Ben"]
scores = [88, 92]
pairs = list(zip(names, scores))
print(pairs)
for name, score in pairs:
print(name, score)A zip is a one-use iterator. Once list(pairs) has taken every pair, nothing is left for the loop. Store the pairs in a list if you need them more than once, or build a fresh zip(...) each time.
Losing items when the lists have different lengths
✗ Wrong
names = ["Asha", "Ben", "Chen"]
scores = [88, 92]
for name, score in zip(names, scores):
print(name, score)✓ Right
names = ["Asha", "Ben", "Chen"]
scores = [88, 92, 79]
for name, score in zip(names, scores, strict=True):
print(name, score)zip stops at the shortest list and drops the rest without a warning. Fix the data, and if the lengths are supposed to match, add strict=True so any future mismatch raises a ValueError instead (Python 3.10+).
9Python enumerate & zip: exercises
Write your answer, then press Check. The checks run in your browser.
1. Number the playlist
Write code Easy ✓ CompletedUse enumerate() to print the songs as a numbered list that starts at 1, using the loop variables number and song. The output must be:
1. Blue Sky
2. Night Drive
3. Paper Boats
4. Slow River
songs = ["Blue Sky", "Night Drive", "Paper Boats", "Slow River"]
# print each song with its number, starting at 1
songs = ["Blue Sky", "Night Drive", "Paper Boats", "Slow River"]
for number, song in enumerate(songs, start=1):
print(f"{number}. {song}")
2. Predict enumerate around zip
Predict the output Medium ✓ CompletedRead the code and type exactly what it will print. Then press Check to run it and compare.
colors = ["red", "green", "blue"]
codes = [1, 2]
for i, (c, n) in enumerate(zip(colors, codes), start=10):
print(i, c, n)
print(list(zip(colors, codes)))10 red 1
11 green 2
[('red', 1), ('green', 2)]
2 more exercises → 6 more examples
Questions learners ask
What is the difference between enumerate() and range(len())?
Both can give you indexes, but enumerate() hands you the index and the item together, so you never have to write items[i]. It reads better, avoids off-by-one slips, and works on anything you can loop over, not only lists.
What happens if the lists given to zip() have different lengths?
zip() stops as soon as the shortest list is used up and ignores the extra items, with no error. In Python 3.10 and newer, zip(a, b, strict=True) raises a ValueError when the lengths differ. If you want the longer list kept and the shorter one padded, look at itertools.zip_longest().
Why does printing a zip or enumerate show something like <zip object at 0x...>?
Both return lazy iterators that create pairs only when asked, so printing one shows the object, not its contents. Wrap it in list() to see the pairs: print(list(zip(a, b))). An iterator can be used only once: after a loop or a list() call has taken the pairs, it is empty.
Further reading
10Next lesson
Up nextPython ListsA list is an ordered, changeable collection that stores many values under one name, each at a numbered position called …Related:
Python for LoopsPython range() FunctionPython DictionariesPython List Comprehensions
← Python Nested Loops · All Control Flow lessons · Practise in the IDE