Pythonのコンテキストマネージャーをわかりやすく解説

コンテキストマネージャーpython
  • withに対応したオブジェクトを「コンテキストマネージャー」という
  • ある処理の前後の処理をまとめて再利用可能にしてくれる
  • コンテキストマネージャーの実体は特殊メソッド__enter__()と__exit__()を実装したクラスのインスタンスである

 

スポンサーリンク

コンテキストマネージャーの構文と実装

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を書いている」...
タイトルとURLをコピーしました