If I create two lists and zip them
a=[1,2,3]
b=[7,8,9]
z=zip(a,b)
Then I typecast z into two lists
l1=list(z)
l2=list(z)
Then the contents of l1 turn out to be fine [(1,7),(2,8),(3,9)], but the contents of l2 is just [].
I guess this is the general behavior of python with regards to iterables. But as a novice programmer migrating from the C family, this doesn’t make sense to me. Why does it behave in such a way? And is there a way to get past this problem?
I mean, yeah in this particular example, I can just copy l1 into l2, but in general is there a way to ‘reset’ whatever Python uses to iterate ‘z’ after I iterate it once?
There’s no way to “reset” a generator. However, you can use
itertools.teeto “copy” an iterator.This involves caching values, so it only makes sense if you’re iterating through both iterables at about the same rate. (In other words, don’t use it the way I have here!)
Another approach is to pass around the generator function, and call it whenever you want to iterate it.
But now you have to bind the arguments to the generator function when you pass it. You can use
lambdafor that, but a lot of people findlambdaugly. (Not me though! YMMV.)I hope it goes without saying that under most circumstances, it’s better just to make a list and copy it.
Also, as a more general way of explaining this behavior, consider this. The point of a generator is to produce a series of values, while maintaining some state between iterations. Now, at times, instead of simply iterating over a generator, you might want to do something like this:
Let’s say this loop may or may not exhaust
z. Now we have a generator in an indeterminate state! So it’s important, at this point, that the behavior of a generator is restrained in a well-defined way. Although we don’t know where the generator is in its output, we know that a) all subsequent accesses will produce later values in the series, and b) once it’s “empty”, we’ve gotten all the items in the series exactly once. The more ability we have to manipulate the state ofz, the harder it is to reason about it, so it’s best that we avoid situations that break those two promises.Of course, as Joel Cornett points out below, it is possible to write a generator that accepts messages via the
sendmethod; and it would be possible to write a generator that could be reset usingsend. But note that in that case, all we can do is send a message. We can’t directly manipulate the generator’s state, and so all changes to the state of the generator are well-defined (by the generator itself — assuming it was written correctly!).sendis really for implementing coroutines, so I wouldn’t use it for this purpose. Everyday generators almost never do anything with values sent to them — I think for the very reasons I give above.