Pythonのwithステートメント
Pythonでは、 ステートメント付き 例外処理で使用され、コードがすっきりして読みやすくなります。ファイル ストリームなどの共通リソースの管理が簡素化されます。 with ステートメントを使用することでコードがどのようにクリーンになるかについては、次のコード例をご覧ください。
Python3
# file handling> # 1) without using with statement> file> => open> (> 'file_path'> ,> 'w'> )> file> .write(> 'hello world !'> )> file> .close()> # 2) without using with statement> file> => open> (> 'file_path'> ,> 'w'> )> try> :> > file> .write(> 'hello world'> )> finally> :> > file> .close()> |
Python3
# using with statement> with> open> (> 'file_path'> ,> 'w'> ) as> file> :> > file> .write(> 'hello world !'> )> |
最初の 2 つの実装とは異なり、with ステートメントを使用するときに file.close() を呼び出す必要がないことに注意してください。 with ステートメント自体により、リソースの適切な取得と解放が保証されます。最初の実装における file.write() 呼び出し中の例外により、ファイルが適切に閉じることができなくなり、コードにいくつかのバグが発生する可能性があります。つまり、ファイル内の多くの変更は、ファイルが適切に閉じられるまで有効になりません。上記の例の 2 番目のアプローチではすべての例外が処理されますが、with ステートメントを使用するとコードがコンパクトになり、はるかに読みやすくなります。したがって、with ステートメントは、リソースを使用するコードが完全に実行されたときにリソースが適切に解放されるようにすることで、バグやリークを回避するのに役立ちます。 with ステートメントは、上に示したようにファイル ストリームや、ロック、ソケット、サブプロセス、Telnet などでよく使用されます。
ユーザー定義オブジェクトでの with ステートメントのサポート
open() には with ステートメントで使用できるようにする特別な機能は何もなく、同じ機能をユーザー定義オブジェクトで提供できます。オブジェクト内で with ステートメントをサポートすると、リソースを開いたままにすることがなくなります。ユーザー定義オブジェクトで with ステートメントを使用するには、オブジェクト メソッドにメソッド __enter__() と __exit__() を追加するだけです。さらに詳しく説明するために、次の例を考えてみましょう。
Python3
# a simple file writer object> class> MessageWriter(> object> ):> > def> __init__(> self> , file_name):> > self> .file_name> => file_name> > > def> __enter__(> self> ):> > self> .> file> => open> (> self> .file_name,> 'w'> )> > return> self> .> file> > def> __exit__(> self> ,> *> args):> > self> .> file> .close()> # using with statement with MessageWriter> with MessageWriter(> 'my_file.txt'> ) as xfile:> > xfile.write(> 'hello world'> )> |
上記のコードを調べてみましょう。お気づきかと思いますが、with キーワードの後にあるのは MessageWriter のコンストラクターです。実行が with ステートメントのコンテキストに入るとすぐに、MessageWriter オブジェクトが作成され、Python は __enter__() メソッドを呼び出します。この __enter__() メソッドでは、オブジェクト内で使用するリソースを初期化します。この __enter__() メソッドは常に、取得したリソースの記述子を返す必要があります。 リソース記述子とは何ですか? これらは、要求されたリソースにアクセスするためにオペレーティング システムによって提供されるハンドルです。次のコード ブロックでは、file はファイル ストリーム リソースの記述子です。
パイソン
file> => open> (> 'hello.txt'> )> |
上記の MessageWriter の例では、 __enter__() メソッドはファイル記述子を作成し、それを返します。ここでの xfile という名前は、__enter__() メソッドによって返されるファイル記述子を参照するために使用されます。取得したリソースを使用するコードのブロックは、with ステートメントのブロック内に配置されます。 with ブロック内のコードが実行されるとすぐに、__exit__() メソッドが呼び出されます。取得したすべてのリソースは __exit__() メソッドで解放されます。これは、ユーザー定義オブジェクトで with ステートメントを使用する方法です。ユーザー定義オブジェクトの with ステートメントのサポートを提供する __enter__() メソッドと __exit__() メソッドのこのインターフェイスは、 コンテキストマネージャー 。
contextlib モジュール
上に示したクラスベースのコンテキストマネージャーは、ユーザー定義オブジェクトで with ステートメントをサポートする唯一の方法ではありません。の コンテキストライブラリ モジュールは、基本的なコンテキスト マネージャー インターフェイスに基づいて構築されたさらにいくつかの抽象化を提供します。ここでは、contextlib モジュールを使用して MessageWriter オブジェクトのコンテキスト マネージャーを書き換える方法を示します。
Python3
from> contextlib> import> contextmanager> class> MessageWriter(> object> ):> > def> __init__(> self> , filename):> > self> .file_name> => filename> > @contextmanager> > def> open_file(> self> ):> > try> :> > file> => open> (> self> .file_name,> 'w'> )> > yield> file> > finally> :> > file> .close()> # usage> message_writer> => MessageWriter(> 'hello.txt'> )> with message_writer.open_file() as my_file:> > my_file.write(> 'hello world'> )> |
このコード例では、 収率 定義内のステートメントでは、関数 open_file() は ジェネレータ関数 。この open_file() 関数が呼び出されると、file という名前のリソース記述子が作成されます。このリソース記述子は呼び出し元に渡され、ここでは変数 my_file によって表されます。 with ブロック内のコードが実行された後、プログラム制御は open_file() 関数に戻ります。 open_file() 関数は実行を再開し、yield ステートメントに続くコードを実行します。コードのこの部分は、yield ステートメントの後に表示され、取得したリソースを解放します。ここでの @contextmanager は デコレータ 。コンテキスト マネージャーの以前のクラスベースの実装とこのジェネレーター ベースの実装は、内部的には同じです。後者の方が読みやすいように見えますが、ジェネレーター、デコレーター、および収量に関する知識が必要です。