There is code like this.
def yielder():
#try:
print('yielder setup')
yield 'text'
#finally:
print('yielder teardown')
def yielder_with_finally():
try:
print('yielder setup')
yield 'text'
finally:
print('yielder teardown')
def f(text):
print(text)
When calling yielder, if you say for y in yielder (): f (y)
, the output will be
yielder setup
text
yielder teardown
However, if you use it so that next is called only once (* 3 of the 4 methods described at the end excluding the former),
yielder setup
text
become that way. teardown is not called. By adding finally, teardown will be called.
However, when calling this function, with for y in yielder_with_finally (): f (y)
or y = yielder_with_finally (); f (next (y))
yielder setup
text
yielder teardown
However, if you call t = next (yielder_with_finally ()); f (t)
or f (next (yielder_with_finally ()))
,
yielder setup
yielder teardown
text
It becomes an unexpected execution order. It behaved similarly in CPython2 / 3. However, if teardown is executed by calling next only once, debugging becomes difficult (in IPython etc.), so it may not be possible to use it in this way in the first place. ** It may be a good idea not to use finally for teardown when yielding. ** **
Note that the yield fixture of pytest is implemented so that it turns twice properly, so this problem does not occur. https://docs.pytest.org/en/latest/_modules/_pytest/fixtures.html
In PyPy, with or without finally, it behaved the same as without finally in CPython.
I verified it with Ruby, but the behavior did not change with or without ensure (similar to PyPy).
def yielder()
return to_enum(:yielder) if !block_given?
#begin
puts('yielder setup')
yield 'text'
#ensure
puts('yielder teardown')
#end
end
def yielder_with_ensure()
return to_enum(:yielder_with_ensure) if !block_given?
begin
puts('yielder setup')
yield 'text'
ensure
puts('yielder teardown')
end
end
def f(text)
puts(text)
end
Recommended Posts