言語バージョン 3.9.0

公開日 January 24, 2021, GMT+9

コード例の出力としてprint関数を使用しています。

使用方法がわからない場合は以下のページを参照してからコード例を確認してください。

print関数の使用方法

with

このページでは豊富な例を用いてPythonのwith文の使い方を学ぶことができます。

with文はコンテキストマネージャ(context manager)を簡潔かつ安全に利用できるPythonの構文です。

コンテキストマネージャとは、特定の処理を行う際に付随する入り口・出口処理を扱うオブジェクトです。 with文は与えられたコンテキストマネージャを起動し、対象になっているオブジェクトの入り口・出口処理を適切なタイミングで実行します。

with文を使用することにより、try・except・finallyを用いる処理を簡潔に記述することができ、 かつ、「ファイルを閉じる」処理に代表されるリソースの開放などの出口処理を確実に実行することによりコードの安全性に寄与します。

# 記述例
with コンテキスト式 as コンテキスト式が生成するオブジェクトを受ける変数:
    # 入り口・出口処理の間に行う処理

TL;DR

基本

# ファイルに文字列を書き込む処理をする場合を考える

# with文を使用する場合

# ファイルオブジェクトはコンテキストマネージャであり、
# with文中の処理が完了すると自動的にcloseメソッドが呼び出される
with open('./sample.log', 'a', encoding='UTF-8') as file:
    file.write('Python')




# with文を使用しない場合.1

# ファイルを開く
file = open('./sample.log', 'a', encoding='UTF-8')
# ファイルに書き込む
file.write('Python')
# ファイルを閉じる
file.close()




# with文を使用しない場合.2

try:
    # ファイルを開く
    file = open('./sample.log', 'a', encoding='UTF-8')
    # ファイルに書き込む
    file.write('Python')
except ValueError as err:
    print(err)
finally:
    # ファイルを閉じる
    file.close()

関連情報:例外処理(try,except,else,finally)の使用方法

独自クラスを用いたwithの挙動チェック

# 独自クラスでコンテキストマネージャを実装する
# __enter__と__exit__が実装されていることが条件
class MyCtxManager:
    def __enter__(self):
        print('__enter__')
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('__exit__')




# with文とコンテキストマネージャの挙動
# 生成したオブジェクトをwith文で使用しない場合はasは指定しなくともよい
with MyCtxManager():
    print('with suite')
# ファイルオブジェクトはcloseメソッドが__exit__メソッドで呼び出されている
==> __enter__
==> with suite
==> __exit__




# withブロック内で例外が発生した場合、__exit__が実行されてから例外のトレースバックが表示される
with MyCtxManager():
    print('with suite')
    raise Exception
------------------------------
__enter__
with suite
__exit__
Traceback (most recent call last):
  ...
Exception
------------------------------

関連情報:class(クラス定義)の使用方法

複数の要素の指定

# コンテキストマネージャA
class ACtxManager:
    def __enter__(self):
        print('A.__enter__')
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('A.__exit__')

# コンテキストマネージャB
class BCtxManager:
    def __enter__(self):
        print('B.__enter__')
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('B.__exit__')



# with文は複数のコンテキストマネージャを指定することができる
with ACtxManager() as a, BCtxManager() as b:
    print('with suite')
==> A.__enter__
==> B.__enter__
==> with suite
==> B.__exit__
==> A.__exit__




# 上記は以下のように記述可能
with ACtxManager() as a:
    with BCtxManager() as b:
        print('with suite')
==> A.__enter__
==> B.__enter__
==> with suite
==> B.__exit__
==> A.__exit__

解説

# 記述例
with コンテキスト式 as コンテキスト式が生成するオブジェクトを受ける変数:
    # 入り口・出口処理の間に行う処理

基本

with文はコンテキストマネージャ(context manager)を起動する構文です。

withの後ろにコンテキスト式(コンテキストマネージャを生成する式)を指定し、 続けてasの後ろに変数を指定することで生成されたコンテキストマネージャをwith文内で扱うことができます。
コンテキストマネージャをwith文内で扱わない場合、asは省略することができます。

with文を使用することで特定のリソースを扱う際の入り口・出口処理の実行を担保することができます。 (入り口処理でエラーが起きる場合を除く)
例えば、コード例のようにopen関数を使用してファイルを編集する処理の場合、最後に「ファイルを閉じる」処理を実行することで安全にリソースを開放することができます。
これは実装者が明示的にファイルオブジェクトのcloseメソッドを呼び出すことでも可能です。 しかし、実装者任せになってしまう点、ファイルオブジェクトがコンテキストマネージャである点を考慮すると、 with文を使用してリソースの開放をPythonの言語機能に任せたほうが適切です。

上記のような一連の処理は try・except・finallyを使用した構文 を簡潔かつ安全なものに書き換えることができます。

コンテキストマネージャを扱う際は可能な限りwith文を用いて記述することを推奨します。

# with文を使用する場合

with open('./sample.log', 'a', encoding='UTF-8') as file:
    # ファイルを閉じる処理は記述しなくともよい
    file.write('Python')




# with文を使用しない場合.1

# ファイルを開く
file = open('./sample.log', 'a', encoding='UTF-8')
# ファイルに書き込む
file.write('Python')
# ファイルを閉じる
file.close()




# with文を使用しない場合.2

try:
    # ファイルを開く
    file = open('./sample.log', 'a', encoding='UTF-8')
    # ファイルに書き込む
    file.write('Python')
except ValueError as err:
    print(err)
finally:
    # ファイルを閉じる
    file.close()

関連情報:例外処理(try,except,else,finally)の使用方法

独自クラスを用いたwithの挙動チェック

コンテキストマネージャは独自に実装することができ、 __enter____exit__を実装しているオブジェクトがコンテキストマネージャとみなされます。

正常系の挙動としては、コンテキストマネージャが生成された直後に__enter__メソッドが実行され、 with文内の最後の処理が終了したタイミングで__exit__メソッドが実行されます。
例えば、ファイルオブジェクトの「ファイルを閉じる」処理を行うcloseメソッドの呼び出しは__exit__メソッド内で行われます。

with文内で例外が発生した場合は、どの段階で例外が生じたかにより挙動が異なります。
より詳しく挙動を知りたい場合は公式ドキュメントの コンテキストマネージャ型 の項を参照してください。

# 独自クラスのコンテキストマネージャ
class MyContext:
    def __enter__(self):
        print('__enter__')
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('__exit__')



# with文とコンテキストマネージャの挙動
with MyContext():
    print('with suite')
==> __enter__
==> with suite
==> __exit__




# withブロック内で例外が発生した場合
with MyContext():
    print('with suite')
    raise Exception
------------------------------
__enter__
with suite
__exit__
Traceback (most recent call last):
  ...
Exception
------------------------------

複数の要素の指定

with文には複数のコンテキストマネージャを指定することも可能です。

複数指定した場合、「後に指定したコンテキストマネージャ」が「先に指定したコンテキストマネージャ」のwith文内にネストされたように扱われます。 それぞれの入り口・出口処理の実行タイミングに気をつけて使用してください。

# コンテキストマネージャA
class ACtx:
    def __enter__(self):
        print('A.__enter__')
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('A.__exit__')

# コンテキストマネージャB
class BCtx:
    def __enter__(self):
        print('B.__enter__')
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('B.__exit__')



# with文は複数のコンテキストマネージャを指定することができる
with ACtx() as a, BCtx() as b:
    print('with suite')
------------------------------
A.__enter__
B.__enter__
with suite
B.__exit__
A.__exit__
------------------------------




# 上記は以下の記述と同じ
with ACtx() as a:
    with BCtx() as b:
        print('with suite')
------------------------------
A.__enter__
B.__enter__
with suite
B.__exit__
A.__exit__
------------------------------

1次情報

with文 - Pythonドキュメント

with文とコンテキストマネージャ - Pythonドキュメント

コンテキストマネージャ型 - Pythonドキュメント

PEP 343 -- The "with" Statement - Python Developer's Guide