I have this generator:
def gen():
rounds = 0
for x in xrange(10):
rounds += 1
yield x
print 'Generator finished (%d rounds)' % (rounds)
If I call it normally:
for x in gen():
pass
I get the expected:
Generator finished (10 rounds)
But if I interrupt the generator, I get nothing:
for x in gen():
break
I would like to get:
Generator interrupted (1 rounds)
Is it possible to detect, in the generator itself, that it has been interrupted from outside? Is there any Exception
that I could catch to detect this event?
You cannot, because a generator's default behaviour is to be interrupted all the time. A generator is constantly paused, every time it yields a value.
In other words, your understanding of how generators work is incorrect. The looping over a generator is entirely outside the control of the generator; all it is tasked to do is produce a next value, which unpauses the code until the next yield
expression is executed.
As such, when no next value is being requested, the generator is paused and cannot execute code to 'detect' that it is not being asked for another value.
What happens when you call a generator function is this:
generator_object.next()
is called..next()
method is called, the function body is run, until a yield
expression is encountered. The function body is paused and the result of the yield
expression is returned as the result of the .next()
method.You can explicitly send messages or raise exceptions in your generator function with the generator_object.send()
and generator_object.throw()
methods, but no such messages or exceptions are sent when not iterating over the generator object.
One thing you could look for is the GeneratorExit
exception thrown inside your generator function when the generator object is closed; see the generator_object.close()
method:
Raises a
GeneratorExit
at the point where the generator function was paused
When a generator object is garbage collected (e.g. nothing references it anymore) the generator_object.close()
method is called automatically:
def gen():
rounds = 0
for x in xrange(10):
rounds += 1
try:
yield x
except GeneratorExit:
print 'Generator closed early after %d rounds' % rounds
raise
print 'Generator finished (%d rounds)' % rounds
Demo:
>>> def gen():
... rounds = 0
... for x in xrange(10):
... rounds += 1
... try:
... yield x
... except GeneratorExit:
... print 'Generator closed early after %d rounds' % rounds
... raise
... print 'Generator finished (%d rounds)' % rounds
...
>>> for i in gen():
... break
...
Generator closed early after 1 rounds
This only works because the returned generator object is only referenced by the for
loop, and is reaped the moment the for
loop ends.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With