LoginSignup
9
6

More than 1 year has passed since last update.

Pythonのwithとcontextmanagerについて調べてみた

Last updated at Posted at 2022-10-11

Pythonを使っていてwith句とcontextmanagerをなんとなく使っていたので、
自身の理解を深めるためにまとめました。

contextmanagerを理解するにはwith句を理解する必要があるっぽい。
なのでまずwithから復習します。

with句について

  • with句は「メインで行いたい処理」の「前後」に処理を埋め込める記述の仕方的なイメージ。
    → 例えばファイルとかデータベースのデータを操作する処理を記述するときとかに役に立つ。
    → ファイルやデータベースのデータを処理する場合、決まってファイルを開いたり、DBとのconnectionを通す必要があり、データの処理が終わった後はファイルを閉じたり、DBとの接続を閉じる必要がある。
    → with句を使わない場合は、例えばファイルの操作とかだと、try/finallyを使って、

    1. ファイルを開く 
    2. ファイルを操作
    3. ファイルを閉じる

を記述する必要がある。

例えば以下ような感じ

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 を使うときの注意点

9
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
6