コンテキストマネージャーの構文と実装
with文で
with コンテキストマネージャー名:
と書きます。
with文の前後に呼ばれる特殊メソッド__enter__()と__exit__()について
- __enter__() … withブロックに入る際に呼ばれる処理が記述される
- __exit__() … withブロックを抜ける際に呼ばれる処理が記述される
class ContextManager: #前処理(withブロックに入る際に呼ばれる) def __enter__(self): print('you entered the with block (__enter__ was called)') #後処理(withブロックを抜ける際に呼ばれる) def __exit__(self, exc_type, exc_value, traceback): print('__exit__() was called') print(f'{exc_type=}') print(f'{exc_value=}') print(f'{traceback=}') ------------------------------------------------------------------- #withブロックが正常終了の場合は、__exit__の引数は全てNone with ContextManager(): print('inside the block') ------------------------------------------------------------------- #出力 you entered the with block (__enter__ was called) inside the block __exit__() was called exc_type=None exc_value=None traceback=None
with文と例外処理
withブロック内で発生した例外は、特殊メソッド__exit__()の引数でその情報を受け取れます。
それゆえ__exit__()内ではraise文は不要です。
上のコンテキストマネージャーを使って、with文内で例外を発生させてみます。
#withブロック内で発生した例外の情報は__exit__()に渡される with ContextManager(): 1 / 0 ------------------------------------------------------------------- #出力 you entered the with block (__enter__ was called) __exit__() was called exc_type=<class 'ZeroDivisionError'> exc_value=ZeroDivisionError('division by zero') traceback= ------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) in 1 #withブロック内で発生した例外の情報は__exit__()に渡される 2 with ContextManager(): ----> 3 1 / 0 ZeroDivisionError: division by zero
asキーワードで__enter__()の戻り値を利用する
コンテキストマネージャーからwithブロック内に渡したい値がある場合、その値を特殊メソッド__enter__()の戻り値にするとasキーワードで受け取れます。
class ContextManager_2: def __enter__(self): return 1 #__enter__()の戻り値がasキーワードに渡される def __exit__(self, exc_type, exc_value, traceback): pass ------------------------------------------------------------------- with ContextManager_2() as f: print(f) ------------------------------------------------------------------- #出力 1
withブロック内から任意の値を特殊メソッド__exit__()に直接渡したい場合
インスタンス変数を介することで、withブロックから値を特殊メソッド__exit__()に渡します。
class Point: def __init__(self, **kwargs): self.value = kwargs #withブロックに入る際に呼ばれる(前処理) def __enter__(self): print('__enter__ was called') return self.value #__enter__()の戻り値がasキーワードに渡される #withブロックから抜ける際に呼ばれる(後処理) def __exit__(self, exc_type, exc_value, traceback): print('__exit__ was called') print(self.value) ------------------------------------------------------------------- with Point(x = 1, y = 2) as p: print(p) p['z'] = 3 ------------------------------------------------------------------- #出力 __enter__ was called {'x': 1, 'y': 2} __exit__ was called {'x': 1, 'y': 2, 'z': 3}
参考書籍:
『Python実践入門ー言語の力を引き出し、開発効率を高める』
陶山嶺 著
WEB+DB PRESS plusシリーズ
技術評論社
Python実践入門 ──言語の力を引き出し、開発効率を高める
Pythonはここ数年で日本語の書籍も増え,開発現場での利用実績も着実に増えてきています。ご自身の第二,第三の言語の選択肢としてPythonが気になっているという方も多いのではないでしょうか。また,「Pythonを始めてみたけど,実際に業務で利用するには不安が残る」「コードレビューに怯えながらPythonを書いている」...