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


回答:


238

はい、finallyブロックの後のtryブロックのように機能します。つまり、常に実行されます(もちろん、Pythonプロセスが異常な方法で終了しない限り)。

これは、ステートメントの仕様であるPEP-343の例の1つでも言及されてい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()全体を配置しないと、呼び出しによってスローされた例外を簡単にキャッチできないことです。withtry..except


8
elsewithそのtry with except問題を解決するために追加することができます。編集:言語に追加
rplnt 2012年

7
関連があるかどうかはわかりませんが、私の知るProcess.terminate()限り、finallyステートメントの呼び出しを保証しない数少ない(唯一の?)シナリオの1つです:「exitハンドラーやfinally節などは、実行されました。」
Rik Poggi

@RikPoggi os._exitは時々使用されます-クリーンアップハンドラーを呼び出さずにPythonプロセスを終了します。
Acumenus 2016年

2
おそらくスネークを少しあざけるかもしれwithませんが、ブロック内からジェネレータ式を返す場合、ジェネレータが値を生成し続ける限り、保証は保持されますか?何かがそれを参照している限り?delつまり、ジェネレータオブジェクトを保持する変数に別の値を使用または割り当てる必要がありますか?
ack 2016年

1
@davidAファイルが閉じられた後も、参照にはアクセスできます。ただし、参照を使用してデータをファイルにプルしたり、ファイルからプッシュしたりすると、次のようになりますValueError: I/O operation on closed file.
RWDJ

36

はい。

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


30
finallyキーロッドから得られる保証を示す興味深い実験は次のとおりdef test(): try: return True; finally: return Falseです。
Ehsan Kia 14

20

はい。より一般的に__exit__は、Withステートメントコンテキストマネージャーのメソッドは、コンテキスト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ます。そのため、コンテキストマネージャはバイパスされません。


4

はい、ただし、__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'
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.