No, there are no built-in functions that avoid this behavior.
What happens is that the zip() function tries to get the next value for all inputs so that it can create the next tuple. He should do this in order, and it is logical that this order matches the arguments passed in. In fact, the order is guaranteed by the documentation :
The iteration order is guaranteed from left to right.
Since the function must support arbitrary iterations, zip() makes no attempt to determine the length of all parameters. He does not know that your second parameter has only 3 elements. It just tries to get the next value for each of the parameters, builds a tuple and returns it. If any of the parameters cannot produce the next value, the zip() iterator is executed. But that means that your generator first will ask for the next item before asking for a list.
Besides changing the order of your inputs, you can instead create your own zip() function, which tries to take it into account, if any:
def limited_zip(*iterables): minlength = float('inf') for it in iterables: try: if len(it) < minlength: minlength = len(it) except TypeError: pass iterators = [iter(it) for it in iterables] count = 0 while iterators and count < minlength: yield tuple(map(next, iterators)) count += 1
So, this version of the zip() function is trying to get the ball at the minimum length of any sequences you went through. This does not protect you from using a shorter iteration in the mix, but works for your test case:
Demo:
>>> gen = iter(range(10)) >>> list(limited_zip(gen, [1, 2, 3])) [(0, 1), (1, 2), (2, 3)] >>> next(gen) 3