[PYTHON] Capture GeneratorExit and detect the end of iteration from the generator side

The iterator itself may not turn to the end due to break etc., but there are rare cases where you want to turn the iteration inside the generator to the end.

Examples that don't work

As an example, the following range_printsum adds the process of printing the sum of the range to the range function.

gen.py


def range_printsum(*args, **kwargs):
    sum_ = 0

    for i in range(*args, **kwargs):
        sum_ += i
        yield i

    print(sum_)

In this generator implementation, if the iteration is stopped in the middle, what is stored in sum_ is the total number actually yield, and sum_ is not output in the first place.

>>> from gen import range_printsum
>>> for i in range_printsum(11):
...     pass
...
55
>>> for i in range_printsum(11):
...     if i > 5:
...         break
...
>>>

Examples that work

Then capture Generator Exit. This is sent when the generator is closed (when the generator's close () is called). This can be captured even if the loop such as for is interrupted. After capturing GeneratorExit, modify range_printsum so that it does not return a value and only adds.

gen.py


def range_printsum(*args, **kwargs):
    sum_ = 0
    stop_yield = False

    for i in range(*args, **kwargs):
        sum_ += i
        if not stop_yield:
            try:
                yield i
            except GeneratorExit:
                stop_yield = True

    print(sum_)

This time it works as expected.

>>> from gen import range_printsum
>>> for i in range_printsum(11):
...     pass
...
55
>>> for i in range_printsum(11):
...     if i > 5:
...         break
...
55
>>>

It works even if it is interrupted like this.

>>> try:
...     for i in range_printsum(11):
...         if i > 5:
...             raise IndexError
... except IndexError:
...     pass
...
55
>>>

A caveat to the generator specifications is that once a generator is closed, it must not generate a value. If you yield any value after capturing GeneratorExit, you will get the error RuntimeError: generator ignored GeneratorExit.

Recommended Posts

Capture GeneratorExit and detect the end of iteration from the generator side
The and operator of Python's evaluation expression seems to be evaluated from the left side expression
Othello ~ From the tic-tac-toe of "Implementation Deep Learning" (4) [End]
DJango Note: From the beginning (simplification and splitting of URLConf)
Combination of recursion and generator
Clean up the repository at the end of the year and delete DS.store
Operate Firefox with Selenium from python and save the screen capture
Find the distance from latitude and longitude (considering the roundness of the earth).
Find the waypoint from latitude and longitude (considering the roundness of the earth).
The story of Python and the story of NaN
Existence from the viewpoint of Python
Find the inertial spindle and moment of inertia from the inertial tensor with NumPy
Summarize the titles of Hottentori at the end and look at the present on the Web