zip(*zipped) sometimes works twice, and sometimes doesn't?In Python, I recently came across this confusing behavior:
zipped = [(1, 'a'), (2, 'b'), (3, 'c')]
unzipped = zip(*zipped)
t1, t2 = map(list, unzipped)
list1, list2 = map(tuple, unzipped) # โ Error: already consumed!
But in another case:
zipped = [(1, 'a'), (2, 'b'), (3, 'c')]
numbers, names = zip(*zipped)
for num, name in zip(numbers, names):
print(f"num={num}, name={name}") # โ
Works
The second example works twice, even though it also uses zip(*zipped)? Why?
Letโs break it down ๐
unzipped = zip(*zipped)
This doesnโt give you a list or a tuple โ it gives you a zip iterator, which is like a one-time-use stream. Once you read from it (like using map(), for, or list()), it's consumed and cannot be reused.
unzipped = zip(*zipped)
print(list(unzipped)) # โ
Works
print(list(unzipped)) # โ Empty: already used
list() or tuple()To reuse the result, convert the zip object to a list (or tuple) right away:
unzipped = list(zip(*zipped))
t1, t2 = map(list, unzipped)
list1, list2 = map(tuple, unzipped) # โ
Now it works!
This is what makes the second example work:
numbers, names = zip(*zipped)
Hereโs whatโs really happening:
zip(*zipped) creates a one-time-use zip object.numbers, names = ...) forces the iterator to run immediately.So:
print(numbers) # (1, 2, 3)
print(names) # ('a', 'b', 'c')
You can reuse numbers and names as many times as you want.
| Case | Type | Reusable? |
|---|---|---|
zip(*zipped) | zip iterator | โ No |
list(zip(*zipped)) | list | โ Yes |
numbers, names = zip(*zipped) | tuple unpack | โ Yes |
Think of zip(*zipped) like a stream of water.
Let me know if this helped you understand zip and unpacking better!
If you found this post useful, feel free to leave a like ๐