以下を考慮してください。
with open(path, mode) as f:
return [line for line in f if condition]
ファイルは適切に閉じられますか、またはreturn
を使用して何らかの形で context manager をバイパスしますか?
はい、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()
呼び出しによってスローされた例外を簡単にキャッチできないことです。
はい。
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 )
はい。より一般的には、 ステートメントコンテキストマネージャーを使用 の__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__
が呼び出されたことを確認します。そのため、コンテキストマネージャはバイパスされません。
はい。ただし、__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'