Python : コンテキストマネージャは with 文の前後で処理を実行する

TwitterFacebookHatena
  • 公開:2021-9-15
  • 更新:2023-10-26
  • 文章量:2703
  • Python

TL;DR

コンテキストマネージャとは、with 文に対応したオブジェクトである。コンテキストマネージャーを使うと、必要なときに正確にリソースを割り当て解放することができる。

コンテキストマネージャのポイント

  • with 文に対応
  • 前後の処理をまとめて再利用可能にする
  • try:finally: の置き換えで利用される
  • close の省略:完了するとファイルが適切に閉じられる
  • __enter__()__exit__()の特殊メソッドを実装している
  • ファイル操作や DB 接続で使われる

一般的なプログラミング言語では、外部のファイルにアクセスする場合、リソースを使った後に close 処理を行う。Python の open でも close 処理が必要だが、with 文を使うと、close を省略することができる。

コンテキストマネージャ とは何か

Python 公式ドキュメントには以下のように書いてある。

コンテキストマネージャ(context manager) とは、 with 文の実行時にランタイムコンテキストを定義するオブジェクトです。コンテキストマネージャは、コードブロックを実行するために必要な入り口および出口の処理を扱います。コンテキストマネージャは通常、 with 文( with 文 の章を参照)により起動されますが、これらのメソッドを直接呼び出すことで起動することもできます。

3. データモデル — Python 3.9.4 ドキュメント

よく分からない。そもそも、コンテキスト(context)という言葉がぼんやりしたので、この意味を調べると「文章などの前後の文脈、状況、背景、状態」といったように色々な解釈が書かれている。

  • 実行中のプログラムの内部状態
  • 状況や関係
  • ンピューターが周囲の環境やユーザーの現在の状況を自動的に把握すること

"コンテキスト"とは何なのか?(栗原潔) - 個人 - Yahoo!ニュース

PEP には、こう書かれている。

Python 言語に「with」という新しいステートメントを追加し、try / finally ステートメントの標準的な使用を除外できるようにします。

PEP 343 -- The "with" Statement | Python.org

つまり、with は、try / finally の代わりに生まれたのである。

open 関数 と with 文

with 文はコンテキストマネージャーを使って、コードのブロックをラップするために使われる。

open 関数を使うと、指定したファイルを開き、ファイルを読み書きするためのオブジェクトを得ることができる。open 関数は with 文と合わせて使い、as の後方の変数にファイルオブジェクトが格納される。

with open('ファイル', 'モード', encoding='エンコード') as f:

以下のコードでは、test.md というファイルに hello と書き込み、次に同じファイルを読み込みモードで開いて内容を出力している。

with open('test.md', 'w') as f:
    f.write('hello')

with open('test.md', 'r') as f:
    a = f.read()

print(a)

# hello
# test.md に書かれた結果が表示される

try:finally: と with の比較

with 文を使うと、try:finally: と同じ制御が行われる。

以下のコードはファイルを開き、データを書き込んだ後に閉じている。ファイルへのデータの書き込み中にエラーが発生した場合は、ファイルを閉じようとする。

f = open('test.md', 'w')

try:
    f.write('try finally')
finally:
    f.close()

# test.md には try finally と書かれる

以下のコードは、with を使った例。コード量が減少している。メリットとしては、ネストされたブロックがどのように終了するか注意することなくファイルが確実に閉じられることである。

with open('test.md', 'w') as f:
    f.write('with')

# test.md には with と書かれる

シェルで実行。読み込みモードのブロック内で書き込みモードを実行し、意図的に例外を発生させたが、ファイルは close されている。

>>> with open('test.md', 'r') as f:
...     f.write('add')
...

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
io.UnsupportedOperation: not writable

>>> print(f.closed)
True

with の前後に呼ばれる特殊メソッド

コンテキストマネージャーは、__enter__()__exit__()の特殊メソッドを実装している。__enter__()は、with ブロックに入るときに呼ばれる処理であり、__exit__()は with ブロックを抜けるときに呼ばれる。

class A:
    def __enter__(self):
        print('before')
    def __exit__(self, exc_type, exc_value, traceback):
        print('after')

with A():
    print('with')

# before
# with
# after

as をつけると値を受け取ることができる

class A:
    def __enter__(self):
        print('before')
        return 1
    def __exit__(self, exc_type, exc_value, traceback):
        print('after')

with A() as f:
    print(f)

# before
# 1
# after

Python : コンテキストマネージャは with 文の前後で処理を実行する