こんにちは、吉政創成 菱沼です。
今回もPythonエンジニア育成推進協会のPython 3 エンジニア認定実践試験の主教材「Python実践レシピ/技術評論社」を使って学習中です。
前回は、よくあるエラーとその対処法について学びました。
今回から3.2に入り、with文について学んでいきます。
with文とは何か。
書籍によれば、元々with文は「try-finallyの利用パターンをカプセル化して再利用するために追加された機能」なんだそうです。そして続けて、「try-finallyに限らず、ある処理の前後の処理を再利用してくれる便利な機能である」と書かれています。
with文といえば、処理を終えた時に必要な後始末を自動でやって行ってくれるという特徴があります。
例えば、open()関数を使ってファイルなどを開いたり、データベースやネットワークに接続したりする場合、close()を必ずすることになります。ですが、with文を使用しておけば、close()をうっかり忘れてしまったとしても、処理が終わったタイミングで自動的に開いたものを閉じてくれます。
書籍にもopen()関数の例が掲載されています。ではここで引用です。
—————————
P.47
open()関数は、ファイルを開いて対応するファイルオブジェクトを返す組み込み関数です。
with文を使用する例
| with open(‘python.txt’) as f: print(f.read()) |
with文を使用しない例
| f = Nonetry: f = open(‘python.txt’) print(f.read())finally: if f: f.close() |
(中略)
asキーワードは、戻り値をwithブロックの中で利用したい場合に指定します。上記の例では、「as f」でファイルオブジェクトを受け取り、withブロック内で利用しています。
—————————
どちらも書き方は違うものの、処理中に例外が発生しても必ず最後にファイルを閉じてくれるようになっていますが、with文を使用した方が圧倒的に短くなっています。
asキーワードは、withブロックの中で使用するために、open() の戻り値に名前を付けるために使われます。名前が必要ない場合は、省略することも可能です。
with文はコンテキストマネージャーを使うための構文
まずは引用から。
—————————
P.47
3.2.2 コンテキストマネージャー(context manager)
with文に渡すオブジェクトをコンテキストマネージャーと呼びます。コンテキストマネージャーとは__enter__()と__exit__()の特殊メソッドを実装したクラスのインスタンスです。
—————————
with文はファイルを開く/閉じるといったそれぞれの動作の前後で必ずやりたい処理を安全に実行するために使用されることになります。
コンテキストマネージャーは、実装されている特殊メソッド__enter__()で処理に入る前に、__exit__()で処理を抜ける時に呼び出されるものだそうです。
先ほどのサンプルコードでいうと、open(‘python.txt’) が返すファイルオブジェクトがコンテキストマネージャーで、withブロックに入る際に __enter__() が呼ばれ、withブロックを抜ける際に __exit__() が呼ばれる、という流れになります。
- コンテキストマネージャーをclassで再現して動きを見てみよう。
ではここで、実際にコンテキストマネージャーを作っているところのサンプルコードを確認してみます。
このサンプルコードではopen()関数と同様にファイルを開き、後処理でファイルを閉じるという処理が行われます。

このサンプルコードは対象のファイルに書かれている内容を表示するものになっていますが、同時に、classを使い、with文が内部でどのような処理(準備→利用→後始末)を行っているかを可視化しています。
実行結果を見ると、ファイルの内容だけでなく、__enter__()と__exit__()がそれぞれwith文に入るタイミングと抜けるタイミングで呼び出されているかがわかります。
もしこれをtry-finallyで書くとすればこういう感じになります。
| try: f = open(‘python.txt’) print(f.read())finally: f.close() |
- withブロック内で例外が発生したらどうなる?
では次にwithブロックの中で例外が発生してしまった場合の例になります。
書籍によれば、withブロックで例外が発生した時は、__exit__()メソッドの引数で例外の情報を受け取ることができ、また、__exit__()の中で例外を処理せずに終了した場合、その例外は再送出されるそうです。
ではこちらがそのサンプルコードです。

実行結果を見て頂けるとわかる通り、withブロックの中で例外が発生したとしても、__exit__() は必ず呼び出されます。この時、__exit__()には、classで設定しておいたtype, value, tracebackとして、発生した例外の情報が引き渡されるようになっています。なので、追加されたprint(f’__exit__(…):{…}’)で発生した例外を表示することができるようになります。
それではきりが良いので、今回はこちらで終了です。お付き合いいただきありがとうございました。
実践試験について知りたい方は以下をご覧ください。
●Python3エンジニア認定実践試験
