概要
Pythonのwith構文で自動でリソースがcloseされる系の処理を、contextlibを利用して定義してみる。
バージョン情報
- Python 3.7.3
contextlib2について
contextlibは古いバージョンでは入っていないこともあるので、そうしたバージョンでも使えるようにcontextlib2というライブラリが用意されている。
複数のバージョンから利用される可能性があるコードを書く際は、バージョンの違いでいらぬトラブルを招かないようにこちらが採用されていることが多い。
contextmanagerで自動でファイルをclose
contextlibを利用しない場合が __enter__ や __exit__ を実装したクラスを用意してwithに渡す。
class AutoCloseFile: def __init__(self, path): self.path = path def __enter__(self): self.f = open(self.path) return self.f def __exit__(self, exception_type, exception_value, traceback): self.f.close() # 上のクラスを使ってファイルを読む with AutoCloseFile('foo.txt') as f: print(f.read()) # 閉じれてる? print(f.closed) #=> True
上記と同じ処理がcontextlibのcontextmanagerでは下記のように書ける。
import contextlib @contextlib.contextmanager def auto_close_file(path): try: f = open(path) yield f finally: f.close() # 上の関数を使ってファイルを読む with auto_close_file('foo.txt') as f: print(f.read()) # 閉じれてる? print(f.closed) #=> True
try 〜 finallyしながらリソースを確保して、yieldでwith statementにリソースを渡す構造。
closingでもう少し簡略に
contextlib.closingを使うと、try〜finalyしてcloseを呼び出すところまでやってくれる。
import contextlib with contextlib.closing(open('foo.txt')) as f: print(f.read()) print(f.closed) #=> True
closingはclose関数が実装されていればなんでも引数に取れる。
# closeが定義されているclass class Foo: def close(self): print('close') # 実行するとcloseが呼び出される with contextlib.closing(Foo()) as f: pass
suppressでエラーを無視した処理
contextlibにはsuppressという例外を握りつぶす機能もある。
例えば下記はFileNotFoundErrorを握りつぶしつつファイルを読んでいる。
with contextlib.suppress(FileNotFoundError): with contextlib.closing(open('bar.txt')) as f: print(f.read())
bar.txtが存在しない場合は、例外が無視されて何も起きずに処理が終わる。
ExitStackで複数のファイルをclose
ExitStackは __exit__ を呼び出す必要があるリソースをstackに積んでおいてスコープが終わったらすべて __exit__ して回る。
たとえば3つのファイルに文字列を出力をするようなコードの場合。
with contextlib.ExitStack() as stack: foo = stack.enter_context(open('foo.txt', 'wt')) bar = stack.enter_context(open('bar.txt', 'wt')) baz = stack.enter_context(open('baz.txt', 'wt')) foo.write('foo') bar.write('bar') baz.write('baz') print(foo.closed) #=> True
このようにExitStackにenter_contextで積んでいけば最後にまとめて __exit__ してくれる。
改定履歴
Author: Masato Watanabe, Date: 2019-07-06, 記事投稿