XXX
XXX

Reputation: 31

why does itertools.count() consume an extra element when used with zip?

I was trying to use functools.partial with itertools.count, by currying zip with itertools.count():

g = functools.partial(zip, itertools.count())

When calling g with inputs like "abc", "ABC", I noticed that itertools.count() mysteriously "jumps".

I thought I should get the same result as directly using zip with itertools.count()? like:

>>> x=itertools.count();
>>> list(zip("abc",x))
[('a', 0), ('b', 1), ('c', 2)]
>>> list(zip("ABC",x))
[('A', 3), ('B', 4), ('C', 5)]

But instead, I get the following -- notice the starting index at the second call of g is 4 instead of 3:

>>> g = functools.partial(zip, itertools.count())
>>> list(g("abc"))
[(0, 'a'), (1, 'b'), (2, 'c')]
>>> list(g("ABC"))
[(4, 'A'), (5, 'B'), (6, 'C')]

Upvotes: 3

Views: 159

Answers (2)

Tim Peters
Tim Peters

Reputation: 70602

Note that you'd get the same result if your original code used arguments in the same order as your altered code:

>>> x = itertools.count()
>>> list(zip(x, "abc"))
[(0, 'a'), (1, 'b'), (2, 'c')]
>>> list(zip(x, "ABC"))
[(4, 'A'), (5, 'B'), (6, 'C')]

zip() tries its first argument first, then its second, then its third ... and stops when one of them is exhausted.

In the spelling just above, after "abc" is exhausted, it goes back to the first argument and gets 3 from x. But its second argument is already exhausted, so zip() stops, and the 3 is silently lost.

Then it moves on to the second zip(), and starts by getting 4 from x.

partial() really has nothing to do with it.

Upvotes: 7

cs95
cs95

Reputation: 402553

It'll be easy to see why if you encapsulate itertools.count() inside a function:

def count():
    c = itertools.count()
    while True:
        v = next(c)
        print('yielding', v)
        yield v

g = functools.partial(zip, count())
list(g("abc"))

The output is

yielding 0
yielding 1
yielding 2
yielding 3
[(0, 'a'), (1, 'b'), (2, 'c')]

You'll see zip will evaluate the next argument from count() (so an extra value 3 is yielded) before it realises there isn't anything else left in the second iterable.

As an exercise, reverse the arguments and you'll see the evaluation is a little different.

Upvotes: 5

Related Questions