このwith
ステートメントは PEP 343 で導入されました。この PEP では、「コンテキスト マネージャー」という新しい用語も導入され、その用語の意味が定義されました。
簡単に言うと、「コンテキスト マネージャー」は、特別なメソッド関数.__enter__()
と.__exit__()
. このwith
ステートメントは.__enter__()
、ステートメントの下にインデントされたコード ブロックを設定するためにメソッドが呼び出されることを保証し、コード ブロックからの終了時にメソッド関数が呼び出されるwith
ことも保証し.__exit__()
ます (ブロックがどのように終了したかに関係なく;たとえば、コードで例外が発生した場合.__exit__()
でも呼び出されます)。
http://www.python.org/dev/peps/pep-0343/
http://docs.python.org/2/reference/datamodel.html?highlight=context%20manager#with-statement-context-managers
このwith
ステートメントは、セットアップとティアダウンが明確に定義されたタスクを処理するための推奨される方法になりました。たとえば、次のようにファイルを操作します。
with open(file_name) as f:
# do something with file
完了すると、ファイルが適切に閉じられることがわかります。
もう 1 つの優れた例は、リソース ロックです。
with acquire_lock(my_lock):
# do something
ロックを取得するまでコードは実行されず、コードが完了するとすぐにロックが解除されることがわかっています。私は Python でマルチスレッド コーディングを行うことはあまりありませんが、行ったときは、このステートメントにより、例外が発生した場合でもロックが常に解放されることが保証されました。
PSコンテキストマネージャーの例をオンラインでGoogle検索したところ、特定のディレクトリでPythonブロックを実行するコンテキストマネージャーという気の利いたものを見つけました。
http://ralsina.me/weblog/posts/BB963.html
編集:
ランタイム コンテキストは、 への呼び出しによってセットアップされ、 への呼び出し.__enter__()
によって破棄される環境です.__exit__()
。ロックを取得する私の例では、コード ブロックはロックを利用できるというコンテキストで実行されます。ファイルを読み取る例では、開いているファイルのコンテキストでコード ブロックが実行されます。
このための秘密の魔法は Python 内部にはありません。特別なスコープ、内部スタック、およびパーサーには特別なものはありません。2 つのメソッド関数を記述するだけ.__enter__()
で.__exit__()
、Python はwith
ステートメントの特定のポイントでそれらを呼び出します。
PEP のこのセクションをもう一度見てください。
PEP 310 は大まかに次の構文を提案していることを思い出してください (「VAR =" の部分はオプションです):
with VAR = EXPR:
BLOCK
これは大まかに次のように変換されます。
VAR = EXPR
VAR.__enter__()
try:
BLOCK
finally:
VAR.__exit__()
どちらの例でも、BLOCK
は、 への呼び出しによってセットアップされ、 によってVAR.__enter__()
破棄される特定のランタイム コンテキストで実行されるコードのブロックですVAR.__exit__()
。
with
ステートメントとその設定方法には、主に 2 つの利点があります。
より具体的な利点は、それが「シンタックス シュガー」であることです。with
私は、6 行の一連のステートメントよりも2 行のステートメントを書きたいと思っています。短いほうが書きやすく、見栄えがよく、理解しやすく、正しく理解するのも簡単です。6 行対 2 行は、物事を台無しにする可能性が高いことを意味します。(そして、このwith
ステートメントの前は、通常、ファイル I/O をtry
ブロックにラップすることについてずさんでした。時々そうするだけでした。今では、常に例外処理を使用with
し、常に例外処理を取得します。)
より抽象的な利点は、これにより、プログラムの設計について考える新しい方法が得られることです。Raymond Hettinger は、PyCon 2013 での講演で次のように述べています。プログラムを作成するとき、関数に分解できる共通部分を探します。次のようなコードがあるとします。
A
B
C
D
E
F
B
C
D
G
関数を簡単に作成できます。
def BCD():
B
C
D
A
BCD()
E
F
BCD()
G
しかし、セットアップ/ティアダウンでこれを行うための本当にきれいな方法はありませんでした。次のようなコードがたくさんある場合:
A
BCD()
E
A
XYZ()
E
A
PDQ()
E
これで、コンテキスト マネージャーを定義して上記を書き直すことができます。
with contextA:
BCD()
with contextA:
XYZ()
with contextA:
PDQ()
これで、プログラムについて考え、「コンテキスト マネージャー」に抽象化できるセットアップ/ティアダウンを探すことができます。Raymond Hettinger は、彼が発明したいくつかの新しい「コンテキスト マネージャー」レシピを示しました (そして、私はあなたのために 1 つまたは 2 つの例を思い出そうと頭を悩ませています)。
編集:わかりました、1つ思い出しました。with
Raymond Hettinger は、ステートメントを使用してブロック内の例外を無視するための、Python 3.4 に組み込まれるレシピを示しました。こちらをご覧ください:https://stackoverflow.com/a/15566001/166949
PS 私は彼が言ったことの意味を伝えるために最善を尽くしました... 私が間違いを犯したり、何かを誤って述べたりした場合、それは彼ではなく私にあります. (そして、彼は時々 StackOverflow に投稿するので、私が何かを台無しにしたら、彼はこれを見て、私を訂正してくれるかもしれません。)
編集:質問を更新してテキストを増やしました。こちらも具体的にお答えします。
これは Beazley が「ランタイム コンテキスト」について話すときに意味することですか? なぜ彼は、ステートメントが「ランタイムコンテキスト内で実行される」と言うのですか??? これは「eval」のようなものですか??
実際にf
は、ブロック内だけにスコープは設定されていません。ステートメントでas
キーワードを使用して名前をバインドすると、その名前はブロックの後もバインドされたままになります。with
「実行時コンテキスト」は非公式な概念であり、「メソッド関数呼び出しによって設定され、.__enter__()
メソッド関数呼び出しによって破棄される状態」を意味し.__exit__()
ます。繰り返しますが、最良の例は、コードが実行される前にロックを取得することです。コードのブロックは、ロックを持つ「コンテキスト」で実行されます。
open が「var に割り当てられていない」オブジェクトを返すことを理解しています?? var に割り当てられないのはなぜですか? そのような発言をすることで、ビーズリーは何を意味するのでしょうか?
オブジェクトがあるとします。それを と呼びましょうk
。 k
は「コンテキスト マネージャー」を実装します。つまり、メソッド関数k.__enter__()
とk.__exit__()
. 今、私たちはこれを行います:
with k as x:
# do something
David Beazley が知っておいてほしいことは、x
必ずしも に拘束されるわけではないということk
です。 x
返されるものにバインドされk.__enter__()
ます。 k.__enter__()
自分自身への参照をk
自由に返すことができますが、それ以外のものを自由に返すこともできます。この場合:
with open(some_file) as f:
# do something
への呼び出しopen()
は、コンテキスト マネージャーとして機能するオープン ファイル オブジェクトを返します。その.__enter__()
メソッド関数は、実際にはそれ自体への参照を返すだけです。
ほとんどのコンテキスト マネージャーは、self への参照を返すと思います。これはオブジェクトであるため、任意の数のメンバー変数を持つことができるため、便利な方法で任意の数の値を返すことができます。しかし、それは必須ではありません。
たとえば、.__enter__()
関数で実行されているデーモンを開始し、関数からデーモンのプロセス ID 番号を返すコンテキスト マネージャーが存在する可能性があり.__enter__()
ます。次に、.__exit__()
関数はデーモンをシャットダウンします。使用法:
with start_daemon("parrot") as pid:
print("Parrot daemon running as PID {}".format(pid))
daemon = lookup_daemon_by_pid(pid)
daemon.send_message("test")
ただし、必要な値を内部に格納して、コンテキスト マネージャー オブジェクト自体を返すこともできます。
with start_daemon("parrot") as daemon:
print("Parrot daemon running as PID {}".format(daemon.pid))
daemon.send_message("test")
.pid
デーモンの PID が必要な場合は、それをオブジェクトのメンバーに入れるだけです。後で何か他のものが必要になった場合は、それをそこに押し込むこともできます。