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.
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
...
>>>
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