Generator

R

R. David Murray

mattia said:
Can you explain me this behaviour:
s = [1,2,3,4,5]
g = (x for x in s)
next(g) 1
s [1, 2, 3, 4, 5]
del s[0]
s [2, 3, 4, 5]
next(g) 3

Why next(g) doesn't give me 2?

Think of it this way: the generator is exactly equivalent to
the following generator function:

def g(s):
for x in s:
yield x

Now, if you look at the documentation for the 'for' statement, there is
a big "warning" box that talks about what happens when you mutate an
object that is being looped over:

There is a subtlety when the sequence is being modified by the loop (this
can only occur for mutable sequences, i.e. lists). An internal counter is
used to keep track of which item is used next, and this is incremented on
each iteration. When this counter has reached the length of the sequence
the loop terminates. This means that if the suite deletes the current (or
a previous) item from the sequence, the next item will be skipped (since
it gets the index of the current item which has already been treated).
Likewise, if the suite inserts an item in the sequence before the current
item, the current item will be treated again the next time through the
loop.

As you can see, your case is covered explicitly there.

If you want next(g) to yield 3, you'd have to do something like:

g = (x for x in s[:])

where s[:] makes a copy of s that is then iterated over.
 
R

R. David Murray

mattia said:
Il Sun, 22 Mar 2009 16:52:02 +0000, R. David Murray ha scritto:
mattia said:
Can you explain me this behaviour:

s = [1,2,3,4,5]
g = (x for x in s)
next(g)
1
s
[1, 2, 3, 4, 5]
del s[0]
s
[2, 3, 4, 5]
next(g)
3


Why next(g) doesn't give me 2?

Think of it this way: the generator is exactly equivalent to the
following generator function:

def g(s):
for x in s:
yield x

Now, if you look at the documentation for the 'for' statement, there is
a big "warning" box that talks about what happens when you mutate an
object that is being looped over:

There is a subtlety when the sequence is being modified by the loop
(this can only occur for mutable sequences, i.e. lists). An
internal counter is used to keep track of which item is used next,
and this is incremented on each iteration. When this counter has
reached the length of the sequence the loop terminates. This means
that if the suite deletes the current (or a previous) item from the
sequence, the next item will be skipped (since it gets the index of
the current item which has already been treated). Likewise, if the
suite inserts an item in the sequence before the current item, the
current item will be treated again the next time through the loop.

As you can see, your case is covered explicitly there.

If you want next(g) to yield 3, you'd have to do something like:

g = (x for x in s[:])

where s[:] makes a copy of s that is then iterated over.

Ok, thanks. Yes, I had the idea that a counter was used in order to
explain my problem. Now I know that my intuition was correct. Thanks.

By the way, it's not the 'for' loop that maintains the counter. It's the
code that implements the iteration protocol for the thing being looped
over. So theoretically you could make a subclass of list that would
somehow handle items being deleted or added in a sensible fashion.
But I doubt it is worth doing that, since the implementation would be
would be pretty idiosyncratic to the use-case.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,994
Messages
2,570,223
Members
46,812
Latest member
GracielaWa

Latest Threads

Top