Search code examples
pythongenerator

Unexpected result from zipping two generators. How does zip work?


I have two generators, one which depends on the output of the other.

def do_work(n):
    for i in range(n):
        yield i


def more_work(x):
    for i in x:
        yield i * 2


def main():
    x = do_work(5)
    y = more_work(x)
    for i, j in zip(x, y):
        print(i, j)


if __name__ == "__main__":
    main()

When I try to zip the inputs, it seems like Python is skipping some of the values of the control variables:

0 2
2 6

Does zip continue iterating before both generators can yield at the same time?


Solution

  • Does zip continue iterating before both generators can yield at the same time?

    Nothing's happening "at the same time" here: there's no concurrency. What the zip does each loop is:

    1. Call next() on x
    2. Call next() on y, which calls next() on x

    So you can see, x is being consumed by two different things, hence it's being exhausted earlier than you expected.

    Possible solution

    You could use itertools.tee, which holds a buffer for you.

    from itertools import tee
    
    ...
    
    def main():
        x = do_work(5)
        x, x1 = tee(x)
        y = more_work(x1)
        for i, j in zip(x, y):
            print(i, j)
    

    Output:

    0 0
    1 2
    2 4
    3 6
    4 8