[Tutor] Strange zip syntax

Brett Ritter swiftone at swiftone.org
Fri Sep 16 04:35:37 CEST 2011


I ran into this article (
http://blog.adku.com/2011/09/hodgepodge-of-python.html ) and found
myself temporarily stymied by one line it in:

zip(*[iter(a)]*2)

Used like this:

>>> a = ['a','1','b','2','c','3']
>>> zip(*[iter(a)]*2)
[('a', '1'), ('b', '2'), ('c', '3')]

While I'm unlikely to use such a construct (if I can't easily follow
it now, I or my successor likely won't follow it should it need to be
debugged sometime in the future), I found the education I got in
deciphering it was worth the effort.  I'm sharing it here so others
can benefit from my puzzlement.

iter(a) returns a list iterator for a.  See help(iter) for more.
[iter(a)] is a list containing one element, an iterator.  This is
created only so we can do the below:
[iter(a)]*2 is a list containing two elements, each the SAME list iterator.
For simplicity, let's break this out for further analysis:

>>> b = iter(a)
>>> c = [b,b]

*[iter(a)]*2 flattens the list when passed into a function call.
Using our more verbose but simple syntax: *c.  This only works when
passed to a function.
zip() creates tuples each holding the Nth elements from a number of
sequences.  See help(zip) for more.
Thus, zip(a) or zip(a,a) would return:
>>> zip(a)
[('a',), ('1',), ('b',), ('2',), ('c',), ('3',)]
>>> zip(a,a)
[('a', 'a'), ('1', '1'), ('b', 'b'), ('2', '2'), ('c', 'c'), ('3', '3')]

What happens when we pass an iterator to zip?  That's not mentioned in
the docstring blurb.
>>> zip(iter(a))
[('a',), ('1',), ('b',), ('2',), ('c',), ('3',)]
Answer: It works as intended.

Now we come to the magic of this little snippet.
zip(iter(a),iter(a)) wouldn't work, because each call to iter(a)
returns a DIFFERENT iterator.
>>> zip(iter(a), iter(a))
[('a', 'a'), ('1', '1'), ('b', 'b'), ('2', '2'), ('c', 'c'), ('3', '3')]

But by creating the list of two elements each of which is the SAME
iterator, as each is asked to iterate it advances the common element
indicator:
>>> zip(*c)
[('a', '1'), ('b', '2'), ('c', '3')]
Notice that the flattening is required, because zip needs to get
multiple arguments:
>>> b = iter(a)  #our original iterator is spent, so we're assigning a new one
>>> c = [b,b]
>>> zip(c)   #Not flattened, is just a single list, like a.
[(<listiterator object at 0x024E32D0>,), (<listiterator object at 0x024E32D0>,)]
>>> zip(b,b)   # here it is two iterators sent to zip() (though they happen to be the SAME iterator)
[('a', '1'), ('b', '2'), ('c', '3')]

I hope some of you enjoy playing with this, and hopefully someone
learned something useful!  While I'm not likely to use the listed
form, I can very well see myself saying:

>>> a = ['a','1','b','2','c','3']   #well, I can see myself using this with meaningful variable names
>>> b = iter(a)
>>> zip(b,b)  # Group in sets of 2 elements

-- 
Brett Ritter / SwiftOne
swiftone at swiftone.org


More information about the Tutor mailing list