[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