在Python中,如果我在“with”块内返回,文件是否仍会关闭?

时间:2012-03-27 07:37:31

标签: python return with-statement

请考虑以下事项:

with open(path, mode) as f:
    return [line for line in f if condition]

文件是否正确关闭,或使用return以某种方式绕过context manager

4 个答案:

答案 0 :(得分:175)

是的,它在finally块之后就像try块一样,即它总是执行(除非python进程以不寻常的方式终止)。

PEP-343的一个示例中也提到了with声明的规范:

with locked(myLock):
    # Code here executes with myLock held.  The lock is
    # guaranteed to be released when the block is left (even
    # if via return or by an uncaught exception).

值得一提的是,您不能轻易捕获open()调用抛出的异常,而不会将整个with块放在try..except块中,这通常不是您想要的。

答案 1 :(得分:28)

def example(path, mode):
    with open(path, mode) as f:
        return [line for line in f if condition]

..几乎相当于:

def example(path, mode):
    f = open(path, mode)

    try:
        return [line for line in f if condition]
    finally:
        f.close()

更准确地说,在退出块时总是调用上下文管理器中的__exit__方法(无论异常,返回等)。文件对象的__exit__方法只调用f.close()(例如here in CPython

答案 2 :(得分:14)

是。更一般地说,With Statement Context Manager__exit__方法确实会在上下文中出现return的情况下被调用。这可以通过以下方式进行测试:

class MyResource:
    def __enter__(self):
        print('Entering context.')
        return self

    def __exit__(self, *exc):
        print('EXITING context.')

def fun():
    with MyResource():
        print('Returning inside with-statement.')
        return
    print('Returning outside with-statement.')

fun()

输出结果为:

Entering context.
Returning inside with-statement.
EXITING context.

上面的输出确认尽管早期__exit__已调用return。因此,不会绕过上下文管理器。

答案 3 :(得分:3)

是的,但是在其他情况下可能会有一些副作用,因为它可能应该在__exit__块中执行某些操作(例如刷新缓冲区)

import gzip
import io

def test(data):
    out = io.BytesIO()
    with gzip.GzipFile(fileobj=out, mode="wb") as f:
        f.write(data)
        return out.getvalue()

def test1(data):
    out = io.BytesIO()
    with gzip.GzipFile(fileobj=out, mode="wb") as f:
        f.write(data)
    return out.getvalue()

print(test(b"test"), test1(b"test"))

# b'\x1f\x8b\x08\x00\x95\x1b\xb3[\x02\xff' b'\x1f\x8b\x08\x00\x95\x1b\xb3[\x02\xff+I-.\x01\x00\x0c~\x7f\xd8\x04\x00\x00\x00'