zip() stops zipping at the shortest of its sequence-arguments and thus also allows unbounded-sequence arguments. This affords, for example, a very spare idiom for the frequent need of a parallel loop on index and sequence-item.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
indices = xrange(sys.maxint) for item, index in zip(sequence, indices): something(item, index) # same semantics as: for index in range(len(sequence)): something(sequence[index], index) # but the change-of-emphasis allows greater # clarity in some usage contexts. # Further alternatives exist of course: class Indexed: def __init__(self, seq): self.seq = seq def __getitem__(self, i): return self.seq[i], i for item, index in Indexed(sequence): something(item, index) # or equivalently: def Indexed(sequence): return zip(sequence, indices)
We often want to loop on a sequence, but also need the current index in the loop body. Canonical is "for i in range(len(sequence)):", using sequence[i] as the item-reference in the body. But in some contexts it's clearer to emphasise the loop on the sequence-item rather than on the index. zip gives an easy alternative, looping on both index and item in parallel, since it truncates at the _shortest_ of its arguments -- thus, it's OK for some arguments to be unbounded sequences. An unbounded sequence of indices is trivial to write (xrange is handy for this!) and a (reusable...) instance of that can be passed to zip, in parallel to the sequence being indexed.
The same zip usage also affords a client-code-transparent alternative to the use of a wrapper-class 'Indexed' -- see the Indexed class and function in the above example code.