The anatomy of a hack to get around try:
/finally:
and generators
Suppose that you have the Python try:/finally: problem and need to get around it, specifically
you need to both be a generator and have your finally:
run
immediately. Taken from here,
one answer is:
def foo(bar): try: def output(): ... compute with bar ... yield res ... return output() finally: print "finalizing"
(Now, there is a lot of things that this doesn't help with; the actual
computation hasn't finished, so you can't do anything in the finally:
that would destroy its ability to work.)
I find this confusing enough that it's worthwhile walking through why it works. It goes like this:
The output()
function is two things; it is both a generator and a
closure. As a closure, it captures the arguments that foo()
was
called with, so that it can do computation with them. As a generator,
calling it doesn't run any of its code but instead
causes Python to immediately return an iterator object that is in turn
effectively capturing the output() closure. The foo()
function then
returns this iterator object and as part of returning, executes its own
finally:
clause.
(The shorter way of putting this is that returning the result of calling
a generator function does not make you a generator function yourself,
and thus your finally:
will run normally and immediately.)
Obviously, you don't have to move all of the code into the output()
closure. In some situations you might have a lot of things that you do
before you can return the first result, and the more you do in the main
function the more resources you can release when it returns.
|
|