[Tutor] Python Idioms?
Steven D'Aprano
steve at pearwood.info
Wed Apr 1 15:01:44 CEST 2015
On Tue, Mar 31, 2015 at 09:50:43PM -0700, Jim Mooney wrote:
> I'm looking at this and can't see how it works, although I understand
> zipping and unpacking. The docs say it's a Python idiom. Does "idiom" mean
> it works in a special way so I can't figure it out from basic principles?
> It looks to me like the iterator in the list gets doubled, so the zip
> should make it [(1,1),(2,2),(3,3),... ], not [(1,2),(3,4),...]
>
> What am I missing here?
The secret is that list multiplication doesn't make *copies*, it
replicates multiple references to the same object.
> >>> s = [1,2,3,4,5,6,7,8]
> >>> list(zip(*[iter(s)]*2))
> >>> [(1, 2), (3, 4), (5, 6), (7, 8)]
Let's pull this apart and see how it ticks. It may actually be more
obvious what is going on if we use more than two references, and do by
hand what * does for us above.
py> s = [100, 200, 300, 400, 500, 600, 700, 800, 900]
py> it = iter(s) # One iterator object.
py> next(it), next(it), next(it) # Call next three times.
(100, 200, 300)
py> args = [it, it, it] # Like [it]*3
py> for x in args:
... print(next(x)) # Like next(it).
...
400
500
600
We created *one* iterator. First we called next(it) three times, by
hand, which yields the first three items in the list s. Then we stick
the iterator in a new list three times, and loop over that, calling
next() each time. That is equivalent to next(it) three more times, which
gives us the next three items.
Finally, we pass them to zip(), as separate arguments:
py> list(zip(*args))
[(700, 800, 900)]
Which gives us the next three items. At that point, we run out of items,
and zip completes.
Putting it (almost) all together now. Remember that `it` above, the
iterator, is now exhausted. It has walked all the way through list s, so
we need a new iterator to make it work again:
py> list(it) # it is exhausted.
[]
py> it = iter(s) # So re-create it.
py> list(zip(*[it]*3))
[(100, 200, 300), (400, 500, 600), (700, 800, 900)]
We can avoid the temporary variable:
py> list(zip(*[iter(s)]*3))
[(100, 200, 300), (400, 500, 600), (700, 800, 900)]
--
Steve
More information about the Tutor
mailing list