yield in try/finally case

Oscar Benjamin oscar.j.benjamin at gmail.com
Thu Mar 3 07:13:36 EST 2016


On 3 March 2016 at 11:52, 刘琦帆 <lqf.txx at gmail.com> wrote:
>
> "A yield statement is not allowed in the try clause of a try/finally construct.  The difficulty is that there's no guarantee the generator will ever be resumed, hence no guarantee that the finally block will ever get executed; that's too much a violation of finally's purpose to bear." from https://www.python.org/dev/peps/pep-0255/
>
> But, meanwhile, the code showed on that page use yield in a try/finally case.
> It really puzzles me. Is there anything wrong?

I think what it means is that you can put a yield in the finally block
but not the try block so:

# Not allowed
def  f():
    try:
        yield 1
    finally:
        pass

# Allowed
def f():
    try:
        pass
    finally:
        yield 1

However that information is out of date. The restriction was removed
in some later Python version. Actually the construct is quite common
when using generator functions to implement context managers:

@contextlib.contextmanager
def replace_stdin(newstdin):
    oldstdin = sys.stdin
    try:
        sys.stdin = newstdin
        yield
    finally:
        sys.stdin = oldstdin

Although the restriction was removed the problem itself still remains.
There's no guarantee that a finally block will execute if there is a
yield in the try block. The same happens if you use a context manager
around a yield statement: the __exit__ method is not guaranteed to be
called. One implication of this is that in the following code it is
not guaranteed that the file will be closed:

def upperfile(filename):
    with open(filename) as fin:
        for line in fin:
            yield line.upper()

--
Oscar



More information about the Python-list mailing list