Difference between throwing GeneratorExit and calling close() in python

Viewed 620

Thank you for opening my question. I tried to make my question clear but if there are still unclear parts because of my English, please let me know.

I am studying on Python coroutine and have read that calling a close() method on generator is similar to throwing GeneratorExit to the generator. So I tried like below.

def gen(n):
    while True:
        yield n

test = gen(10)
next(test)
test.throw(GeneratorExit)

Then GeneratorExit exception occurred. However when I tried test.close(), it did not raise any exception.

So I modified above code slightly;

def gen(n):
    while True:
        try:
            yield n
        except GeneratorExit:
            break
test = gen(10)
next(test)
test.throw(GeneratorExit)

As GeneratorExit was handled, it was not raised but StopIteration exception occurred. I understand that if there is no more yield, StopIteration exception rises. However it was not raised when I tried test.close() again with the modified code.

Could you let me know what the difference between throwing GeneratorExit and calling close() method is?

To be more precise, I can understand why StopIteration and GeneratorExit exception occur with test.throw(GeneratorExit) but don't know why those exceptions are not raised when using test.close()

Thanks.

1 Answers

GeneratorExit occurs in one of two cases:

  • When you're calling close
  • When python calls the garbage collector for that generator.

(see the documentation)

You can see that in the following code:

def gen(n):
    while True:
        try:
            yield n
        except GeneratorExit:
            print("gen caught a GeneratorExit exception")
            break # (throws a StopIteration exception)

def gen_rte(n):
    while True:
        try:
            yield n
        except GeneratorExit:
            print("gen_rte caught a GeneratorExit exception")
            # No break here - we'll get a runtime exception

test = gen(10)
print(next(test))
==> 10

test.close()
==> gen caught a GeneratorExit exception  


test = gen(15)
print(next(test))
==> 15
test.throw(GeneratorExit)

==> gen caught a GeneratorExit exception Traceback (most recent call last): File "...", line 20, in test.throw(GeneratorExit) StopIteration (this is the result of the 'break' statement

test = gen_rte(20)
print(next(test))
==> 20

test.close()
==> 
    gen_rte caught a GeneratorExit exception
    Traceback (most recent call last):
      File "...", line 24, in <module>
        test.close()
    RuntimeError: generator ignored GeneratorExit

Finally, there's another GeneratorExit exception before the program ends - I believe it's a result of the garbage collector. gen_rte caught a GeneratorExit exception Exception ignored in: RuntimeError: generator ignored GeneratorExit

Related