enumerate and zip
Picture a theater cloakroom. The attendant takes each coat and hands out a numbered tag for it: number 1 for the first coat, number 2 for the second. Each visitor ends up with a pair: a tag and their coat.
Programs need such pairs all the time. The loop for name in names gives the items of a list, but not their numbers. You have to keep the number in a separate variable or go through the indices with range(len(names)). The enumerate function works like the attendant and gives the number together with the item:
names = ["ann", "bob", "cid"]
for i, name in enumerate(names, 1):
print(f"{i}.{name}", end=" ") # 1.ann 2.bob 3.cidLet's go through the loop header:
enumerate(names, 1)gives the pairs(1, 'ann'),(2, 'bob')and(3, 'cid').for i, name inunpacks each pair: the number goes toi, the name toname.- The loop body prints the number and the name with a dot between them.
Pairs from enumerate
On each iteration, enumerate gives a tuple of two items: the number and the item. What to know about these pairs:
- the header
for i, name inunpacks the tuple into two names, like an ordinary assignment; - the second argument sets the first number, and without it counting starts at
0, like indices; enumerateaccepts any iterable:enumerate("ab")gives the pairs(0, 'a')and(1, 'b').
print(enumerate(names)) does not show the pairs but prints a string like <enumerate object at 0x…>. This is where the cloakroom analogy stops working. The attendant hands out all the tags at once. enumerate gives the pairs one at a time, only when the loop asks for them.
The list() function collects all the pairs into a list that you can print and index:
numbered = list(enumerate(names))
print(numbered) # [(0, 'ann'), (1, 'bob'), (2, 'cid')]
print(numbered[-1][0]) # 2numbered[-1] is the last tuple of the list, (2, 'cid'), and [0] takes the number from it.
zip
The zip function is named after a zipper. When you close a zipper, the teeth of its two halves lock in pairs: the first with the first, the second with the second. In the same way, zip goes through several sequences at once. At each step, it gives a tuple of their items at the same index:
scores = [90, 75]
pairs = list(zip(names, scores))
print(pairs) # [('ann', 90), ('bob', 75)]
for name, score in zip(names, scores):
print(name, score, end="; ") # ann 90; bob 75;What happens in the example:
zip(names, scores)locks each name with the score at the same index;list()collects the pairs into a list, as withenumerate;- the loop
for name, score inunpacks each pair into two names.
The two halves of a real zipper have the same length, but lists do not have to. zip stops when the shortest sequence runs out. There is no error: "cid" has no score, and that name does not get into the pairs.
Interviewers often ask about this: "What does zip do with lists of different lengths?" The answer: it silently drops the extra items. If such a loss means an error in the data, compare the lengths before the loop: len(names) == len(scores). Another way is zip(names, scores, strict=True): then Python reports a ValueError when the lengths differ.
zip also accepts three sequences or more: then each tuple holds one item from each. A ready list of pairs is looped over with unpacking too: for name, score in pairs: gives the same names and scores as the zip loop.
Common mistake
Two sequences are listed in the for header separated by a comma, without zip:
names = ["ann", "bob", "cid"]
scores = [90, 75, 60]
for name, score in names, scores: # ✗
print(name, score)ValueError: too many values to unpack (expected 2)names, scores is a tuple of two lists, and the loop goes through the lists, not their items. On the first iteration, it tries to unpack the whole names list into two names, and that list has three items.
If each list had two items, there would be no error. But then name and score would get two names first, then two scores, not a name with its score. The correct header is for name, score in zip(names, scores):.
Key points
enumerategives "number, item" pairs; the second argument sets the first number,0by default.ziplocks the items of several sequences at the same index into tuples.zipstops at the shortest sequence without an error;strict=Trueturns a length mismatch into aValueError.- Pairs from
enumerateandzipare spread over names right in theforheader.
Subscribe to take the course
The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.