12

(より明確にするために編集されています)

私は Python の本 ( Python Essential Reference by Beazley ) を読んでいて、彼は次のように言っています。

このwithステートメントを使用すると、一連のステートメントを 、コンテキスト マネージャーとして機能するオブジェクトによって制御されるランタイム コンテキスト内で実行できます。

次に例を示します。

with open("debuglog","a") as f:
    f.write("Debugging\n")
    statements
    f.write("Done\n")

彼は続けてこう言います:

with obj ステートメントは、オプションの as var 指定子を受け入れます。指定された場合、obj._ enter _() によって返された値が var に配置されます。obj が必ずしも var に割り当てられる値ではないことを強調することが重要です。

「with」キーワードの仕組みを理解しています。ファイルオブジェクトはopenによって返され、そのオブジェクトはブロックの本体内でfを介してアクセスできます。また、 enter ()と最終的にexit ()が呼び出されることも理解しています。

しかし、ランタイム コンテキストとは正確には何でしょうか。いくつかの低レベルの詳細があればいいでしょう-または、Cの例です。誰かが「コンテキスト」とは何か、それが他の言語(C、C++)とどのように関連するかを明確にすることができますか? コンテキストについての私の理解は環境でした。たとえば、Bashシェルはすべての ( envが表示された) シェル変数のコンテキストでlsを実行します。withキーワードを使用すると - はいfブロックの本体にアクセスできますが、それは単なるスコープではありませんか? 例: for x in y:ここでxはブロック内にスコープが設定されておらず、ブロック外の値を保持していますブロック内でのみスコープが設定され、with-block の外ではすべての重要性が失われます?? なぜ彼は、ステートメントが「ランタイムコンテキストで実行される」と言うのですか??? これは「eval」のようなものですか??

「varに割り当てられていない」openオブジェクトを返すことを理解しています?? var に割り当てられないのはなぜですか? そのような発言をすることで、ビーズリーは何を意味するのでしょうか?

4

2 に答える 2

10

この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つ思い出しました。withRaymond Hettinger は、ステートメントを使用してブロック内の例外を無視するための、Python 3.4 に組み込まれるレシピを示しました。こちらをご覧ください:https://stackoverflow.com/a/15566001/166949

PS 私は彼が言ったことの意味を伝えるために最善を尽くしました... 私が間違いを犯したり、何かを誤って述べたりした場合、それは彼ではなく私にあります. (そして、彼は時々 StackOverflow に投稿するので、私が何かを台無しにしたら、彼はこれを見て、私を訂正してくれるかもしれません。)

編集:質問を更新してテキストを増やしました。こちらも具体的にお答えします。

これは Beazley が「ランタイム コンテキスト」について話すときに意味することですか? なぜ彼は、ステートメントが「ランタイムコンテキスト内で実行される」と言うのですか??? これは「eval」のようなものですか??

実際にfは、ブロック内だけにスコープは設定されていません。ステートメントでasキーワードを使用して名前をバインドすると、その名前はブロックの後もバインドされたままになります。with

「実行時コンテキスト」は非公式な概念であり、「メソッド関数呼び出しによって設定され、.__enter__()メソッド関数呼び出しによって破棄される状態」を意味し.__exit__()ます。繰り返しますが、最良の例は、コードが実行される前にロックを取得することです。コードのブロックは、ロックを持つ「コンテキスト」で実行されます。

open が「var に割り当てられていない」オブジェクトを返すことを理解しています?? var に割り当てられないのはなぜですか? そのような発言をすることで、ビーズリーは何を意味するのでしょうか?

オブジェクトがあるとします。それを と呼びましょうkkは「コンテキスト マネージャー」を実装します。つまり、メソッド関数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 が必要な場合は、それをオブジェクトのメンバーに入れるだけです。後で何か他のものが必要になった場合は、それをそこに押し込むこともできます。

于 2013-10-29T08:10:07.043 に答える
3

with context は、エントリ時に__enter__メソッドが呼び出され、指定されたが返さvarれるものに設定されるようにし__enter__ます。

ほとんどの場合、これは以前に処理されたオブジェクトです (ファイルの場合はそうです) が、たとえばデータベースでは、接続オブジェクトではなく、カーソル オブジェクトが返されます。

ファイルの例は、次のように拡張できます。

f1 = open("debuglog","a")
with f1 as f2:
    print f1 is f2

これは次のように出力されます。Trueファイル オブジェクトは によって返され__enter__ます。(その観点から、self.)

データベースは次のように機能します

d = connect(...)
with d as c:
    print d is c # False
    print d, c

ここで、dcは完全に異なります。dはデータベースへの接続であり、cは 1 つのトランザクションに使用されるカーソルです。

節は、節の実行状態 (成功または失敗) が与えられwithた呼び出しによって終了します。__exit__()この場合、__exit__()メソッドは適切に動作します。

ファイルの例では、エラーの有無に関係なく、ファイルは閉じられます。

データベースの例では、通常、トランザクションは成功するとコミットされ、失敗するとロールバックされます。

コンテキスト マネージャーは、ファイル、データベースなどのようなものを簡単に初期化およびクリーンアップするためのものです。

私が認識している C または C++ での直接的な対応はありません。

C は例外の概念を認識していないため、 a にキャッチされるものはありません__exit__()。C++ は例外を認識しており、そうする方法があるようです (以下のコメントを参照)。

于 2013-10-29T08:16:12.883 に答える