I was writing an answer to this question when noticed that my simple implementation didn’t produce correct results. While hunting down the bug, I noticed the following:
In [1]: import itertools
In [2]: gen = itertools.cycle((0,1,2))
In [3]: zip(gen, range(3))
Out[3]: [(0, 0), (1, 1), (2, 2)]
In [4]: zip(gen, range(3))
Out[4]: [(1, 0), (2, 1), (0, 2)]
For whatever reason, gen‘s next() method is called one additioinal time.
To illustrate this, I used the following:
class loudCycle(itertools.cycle):
def next(self):
n = super(loudCycle, self).next()
print n
return n
In [6]: gen = loudCycle((0,1,2))
In [7]: zip(gen, range(3))
0
1
2
0
Out[7]: [(0, 0), (1, 1), (2, 2)]
This happens because
zipevaluates iterators from left to right, meaning that, after three steps, it callsnext()ongenand only then oniter(range(3))(or something like that) and encounters aStopIteration. To get around this, use the shorter (finite) iterable as the left-most argument: