In your first example, you were creating a new generator each time:
x().next()
This starts the generator from the top, so the first statement. When a == 3, the exception is raised, otherwise the generator just yields and pauses.
When you assigned your generator later on, the global a started at 5, the code then continued from where it left of until it ends or comes across another yield statement, then ended. When a generator function ends, it raises StopIteration.
Let's break this down into steps:
a = 5. You create new generator and call .next() on it. The following code is executed:
global a if a == 3: # False raise Exception("Stop") a = a - 1 # a is now 4 yield a
The generator is paused on the last line, and 4 is yielded.
You create a new generator and call .next() on it. a is 4 at the start:
global a if a == 3: # False raise Exception("Stop") a = a - 1 # a is now 3 yield a
The generator is paused on the last line, and 3 is yielded.
You create a new generator and call .next() on it. a is 3 at the start:
global a if a == 3: # True raise Exception("Stop")
An exception is raised.
You set a = 5 again.
You create a new generator, store a reference in b and call .next() on it. The following code is executed:
global a if a == 3: # False raise Exception("Stop") a = a - 1 # a is now 4 yield a
The generator is paused on the last line, and 4 is yielded.
You call .next() again on the same, existing generator referenced by b. The code resumes at the paused point.
The function has no more code at that point, and returns. StopIteration is raised.
If you were to use a loop instead, you'd see the difference better:
>>> def looping(stop): ... for i in looping(stop): ... yield i ... >>> looping(3).next() 0 >>> looping(3).next() 0
Note how each time I create a new generator, the loop starts from the beginning. Store a reference however, and you'll notice it continue instead:
>>> stored = looping(3) >>> stored.next() 0 >>> stored.next() 1 >>> stored.next() 2 >>> stored.next() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
During the loop, each time the yield expression is executed, the code is paused; calling .next() continues the function where it left of the previous time.
The StopIteration exception is entirely normal; it is how generators communicate that they are done. A for loop looks for this exception to end the loop:
>>> for i in looping(3): ... print i ... 0 1 2