Pythonを使っていてwith句とcontextmanagerをなんとなく使っていたので、
自身の理解を深めるためにまとめました。
contextmanagerを理解するにはwith句を理解する必要があるっぽい。
なのでまずwithから復習します。
with句について
-
with句は「メインで行いたい処理」の「前後」に処理を埋め込める記述の仕方的なイメージ。
→ 例えばファイルとかデータベースのデータを操作する処理を記述するときとかに役に立つ。
→ ファイルやデータベースのデータを処理する場合、決まってファイルを開いたり、DBとのconnectionを通す必要があり、データの処理が終わった後はファイルを閉じたり、DBとの接続を閉じる必要がある。
→ with句を使わない場合は、例えばファイルの操作とかだと、try/finallyを使って、- ファイルを開く
- ファイルを操作
- ファイルを閉じる
を記述する必要がある。
例えば以下ような感じ
try:
1. ファイルを開く処理(前処理)
2. ファイルを編集する処理(メインでやりたい処理)
finally:
3. ファイルを閉じる処理(後処理)
→ with句とは、そのようなtry/finallyを置き換えるもの
上の処理をwith句で書くと、
with open(file_path) as f:
f(開かれたファイル)を編集する処理(メインでやりたい処理)
普通のクラスに__enter__(), __exit__()をつけるだけでwith句で利用できるクラス(カスタムマネージャー)ができる
→ __enter__()は前処理、__exit__()は後処理を記述するメソッドのよう。
→ ファイル操作に当てると、__enter__()にファイルを開く処理を記述。__exit__()にファイルを閉じる処理を記述みたいに書くと、ファイル操作のソースコードがかなりシンプルに記述できる。
class FileOpen:
def __enter__():
1. ファイルを開く処理
return { 開いたファイル } ⇦ これでwith句の as のところにファイルを開いたファイルを渡せる
def __exit__():
3. ファイルを閉じる処理
with FileOpen() as f:
2. f(開かれたファイルを)編集する処理
→ プラスで実は__exit__()の書き方を工夫すると、エラーハンドリングもできる模様。
→ __exit__(self, exc_type, exc_value, traceback)みたいに書くと、エラーハンドリングができるっぽい。
class A:
def __enter__():
前処理を記述
def __exit__(self, exc_type, exc_value, traceback):
if exc_type is None:
後処理を記述
else:
if exc_type == "xxxError":
return { エラーメッセージとかエラーコードとか }
→ なのでwith句用のクラスを作れば、try/except/fanallyの記述をせずともそれと同じことができるようになる。
→ ただこのようなクラスを作るのも面倒だし、もっとシンプルに直感的に書きたいからcontexlibのcontextmanagerを使おうという感じ。
contextmanagerについて
- メソッドの上に@contextmanagerをつけることで(デコレータという)、with句で利用できるメソッド簡単に作れるよ。※クラスではなくメソッド
→ ざっくりいうと、yieldの前に書いたものが前処理、yieldの後に書いたものが後処理といった感じ
→ yiledは、前処理が終わった後にwith句のブロックの中のメインの処理に一旦戻るよ的なもの
→ メインの処理が終わるとまた、@contextmanagerの関数に戻ってきて、yieldの後に書いた処理が実行される
→ なのでファイルの操作だと、1. yieldの前にファイル開く処理を書く、2. メインの処理でファイル操作処理を書く、3. yieldの後にファイルを閉じるの処理を書くみたいな感じ
from contextlib import contextmanager
@contextmanager
def file_open():
1. ファイルを開く処理
2. yield {開いたファイル}
3. ファイルを閉じる処理
with file_open() as f:
2. f(開かれたファイル)を編集する処理(メインでやりたい処理)
→ ただし@contextmanagerの書き方だとメソッドとして記述するので、上の__exit__()みたいな関数でエラーハンドリングをするとかはないので、
try/exceptは自分で書きましょうね。でも直感的にシンプルに書けるのでこっちの方がいいよね。という感じと理解しました。
from contextlib import contextmanager
@contextmanager
def file_open():
try:
1. ファイルを開く処理
2. yield {開いたファイル}
3. ファイルを閉じる処理
except xxxError as e
return { エラーメッセージとかエラーコードとか }
with file_open() as f:
2. f(開かれたファイル)を編集する処理(メインでやりたい処理)
参考
Pythonのコンテキストマネージャってなんなの?と思って調べた話
Python のコンテキストマネージャと with ブロック
Python : コンテキストマネージャは with 文の前後で処理を実行する
Python: contextlib.contextmanager を使うときの注意点