80

I know how to use the zip() function in Python 3. My question is regarding the following which I somehow feel quite peculiar:

I define two lists:

lis1 = [0, 1, 2, 3]
lis2 = [4, 5, 6, 7]

and I use the zip() on these in the following ways:

1. test1 = zip( lis1, lis2)

2. test2 = list(zip(lis1, lis2))

when I type test1 at the interpreter, I get this:

"zip object at 0x1007a06c8"

So, I type list(test1) at the interpreter and I get the intended result, but when I type list(test1) again, I get an empty list.

What I find peculiar is that no matter how many times I type test2 at the interpreter I always get the intended result and never an empty list.

General Grievance
  • 4,555
  • 31
  • 31
  • 45
dhaliman
  • 1,542
  • 1
  • 12
  • 23
  • 2
    A bit shorter in Python 3: `test2 = [*zip(lis1, lis2)]`. – Friedrich -- Слава Україні Mar 11 '20 at 09:01
  • 2
    And `test2 = *zip(lis1, lis2),` unpacks the zip-iterator into a tuple (Python 3). – Friedrich -- Слава Україні Mar 11 '20 at 09:15
  • I'm reopening this because it isn't *just* about the general issue of how iterators work in Python; it's specifically about the fact **that** `zip` produces an iterator **in 3.x**, as opposed to a list (as it did in 2.x). I haven't been able to find a better version of the question that focuses on only the latter part. – Karl Knechtel Jan 08 '23 at 11:57
  • @KarlKnechtel: agreed, I can't find one either, but there are several posts that (now) point to this one. – Martijn Pieters Jan 08 '23 at 12:21
  • @MartijnPieters I found it: [TypeError: 'zip' object is not subscriptable](https://stackoverflow.com/questions/27431390). It needs a little editing to be useful as a canonical, but other stuff is probably much better closed to there, and this is closable to both (and not a good target). – Karl Knechtel Jan 14 '23 at 10:26

2 Answers2

115

Unlike in Python 2, the zip function in Python 3 returns an iterator. Iterators can only be exhausted (by something like making a list out of them) once. The purpose of this is to save memory by only generating the elements of the iterator as you need them, rather than putting it all into memory at once. If you want to reuse your zipped object, just create a list out of it as you do in your second example, and then duplicate the list by something like

 test2 = list(zip(lis1,lis2))
 zipped_list = test2[:]
 zipped_list_2 = list(test2)
Blair
  • 6,623
  • 1
  • 36
  • 42
  • One of the most important words of this great explanation is the word "once". Even testing the length of the zipped object `len(list(zipped))` will exhaust it. – DaveL17 Feb 11 '22 at 04:05
31

The zip() function in Python 3 returns an iterator. That is the reason why when you print test1 you get - <zip object at 0x1007a06c8>. From documentation -

Make an iterator that aggregates elements from each of the iterables.

But once you do - list(test1) - you have exhausted the iterator. So after that anytime you do list(test1) would only result in empty list.

In case of test2, you have already created the list once, test2 is a list, and hence it will always be that list.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Anand S Kumar
  • 88,551
  • 18
  • 188
  • 176
  • isn't that behavior applicable for a generator, not an iterator? – Jeremy Fisher Jul 28 '15 at 18:37
  • why isn't it applicable for an iterator? Check - http://stackoverflow.com/questions/2776829/difference-between-pythons-generators-and-iterators - *Every generator is an iterator* . Iterators also can get exhausted, and in the same way , there can be generators that never get exhausted. – Anand S Kumar Jul 28 '15 at 18:44