web-dev-qa-db-ja.com

Pythonでは、「with」ブロック内に戻ると、ファイルはまだ閉じますか?

以下を考慮してください。

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

ファイルは適切に閉じられますか、またはreturnを使用して何らかの形で context manager をバイパスしますか?

207
Lightbreeze

はい、finallyブロックの後のtryブロックのように動作します。つまり、常に実行されます(pythonプロセスが異常な方法で終了しない限り)。

withステートメントの仕様である PEP-343 の例の1つでも言及されています。

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).

ただし、言及する価値があるのは、try..exceptブロック全体にwithブロック全体を入れない限り、open()呼び出しによってスローされた例外を簡単にキャッチできないことです。

186
ThiefMaster

はい。

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()を呼び出すだけです(例 ここではCPython

29
dbr

はい。より一般的には、 ステートメントコンテキストマネージャーを使用__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.

上記の出力は、初期のreturnにもかかわらず__exit__が呼び出されたことを確認します。そのため、コンテキストマネージャはバイパスされません。

15
Acumenus

はい。ただし、__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'
3
virusdefender