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

Lig*_*eze 222 python return with-statement

考虑以下:

with open(path, mode) as f:
    return [line for line in f if condition]
Run Code Online (Sandbox Code Playgroud)

文件是否会正确关闭,或者以return某种方式绕过上下文管理器

Thi*_*ter 202

是的,它就像一个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).
Run Code Online (Sandbox Code Playgroud)

值得一提的是,你不能轻易地捕获open()调用引发的异常,而不是将整个with块放在一个try..except通常不是你想要的块中.

  • `else`可以添加到`with`来解决`try with except`问题.编辑:添加到语言 (8认同)
  • 我不知道它是否相关,但据我所知`Process.terminate()`是少数(唯一的?)场景之一,不能保证调用`finally`语句:*"注意退出处理程序和最后的条款等将不会被执行."* (7认同)
  • 也许稍微嘲弄蛇,但是如果我从`with`块中返回一个生成器表达式,只要生成器保持产生值,保证是否保持?只要有什么参考吗?即我需要使用`del`或为保存生成器对象的变量分配不同的值? (2认同)

dbr*_*dbr 30

是.

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

..几乎相当于:

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

    try:
        return [line for line in f if condition]
    finally:
        f.close()
Run Code Online (Sandbox Code Playgroud)

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

  • 一个有趣的实验来证明你从`finally` keywrod获得的保证是:`def test():try:return True; 最后:返回False. (28认同)

Acu*_*nus 18

是.更一般地说,在语境内部确实会调用With语句上下文管理器__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()
Run Code Online (Sandbox Code Playgroud)

输出是:

Entering context.
Returning inside with-statement.
EXITING context.
Run Code Online (Sandbox Code Playgroud)

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


vir*_*der 7

是的,但在其他情况下可能会有一些副作用,因为它可能应该在__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'
Run Code Online (Sandbox Code Playgroud)