481

with今日初めてPythonステートメントに出くわしました。私は数ヶ月間 Python を軽く使っていましたが、その存在すら知りませんでした! ややあいまいなステータスを考えると、質問する価値があると思いました:

  1. 使用するように設計された Pythonwithステートメントは何ですか?
  2. それを何のために利用しますか?
  3. 知っておく必要がある落とし穴や、その使用に関連する一般的なアンチパターンはありますか? try..finallyよりも優れた使用例はありwithますか?
  4. なぜもっと広く使われていないのですか?
  5. どの標準ライブラリ クラスと互換性がありますか?
4

11 に答える 11

434
  1. これは私の前に他のユーザーによってすでに回答されていると思うので、完全を期すために追加するだけです。withステートメントは、いわゆるコンテキストマネージャーで一般的な準備およびクリーンアップタスクをカプセル化することにより、例外処理を簡素化します。詳細はPEP 343にあります。たとえば、openステートメントはそれ自体がコンテキスト マネージャーであり、ファイルを開き、それを使用したステートメントのコンテキスト内で実行が行われている限りファイルを開いたままにしwith、コンテキストを離れるとすぐにファイルを閉じることができます。例外のため、または通常の制御フロー中にそれを残したかどうかに関係なく。このステートメントは、C++のRAII パターンwithと同様の方法で使用できます。withwithステートメントとコンテキストを離れるときに解放されます。

  2. 例としては、 を使用してファイルを開く、 を使用with open(filename) as fp:してロックを取得するwith lock:(lockは のインスタンスthreading.Lock)。contextmanagerのデコレータを使用して、独自のコンテキスト マネージャを作成することもできますcontextlib。たとえば、現在のディレクトリを一時的に変更し、元の場所に戻る必要がある場合によく使用します。

    from contextlib import contextmanager
    import os
    
    @contextmanager
    def working_directory(path):
        current_dir = os.getcwd()
        os.chdir(path)
        try:
            yield
        finally:
            os.chdir(current_dir)
    
    with working_directory("data/stuff"):
        # do something within data/stuff
    # here I am back again in the original working directory
    

    を一時的にリダイレクトしsys.stdin、後でそれらを復元する別の例を次に示します。sys.stdoutsys.stderr

    from contextlib import contextmanager
    import sys
    
    @contextmanager
    def redirected(**kwds):
        stream_names = ["stdin", "stdout", "stderr"]
        old_streams = {}
        try:
            for sname in stream_names:
                stream = kwds.get(sname, None)
                if stream is not None and stream != getattr(sys, sname):
                    old_streams[sname] = getattr(sys, sname)
                    setattr(sys, sname, stream)
            yield
        finally:
            for sname, stream in old_streams.iteritems():
                setattr(sys, sname, stream)
    
    with redirected(stdout=open("/tmp/log.txt", "w")):
         # these print statements will go to /tmp/log.txt
         print "Test entry 1"
         print "Test entry 2"
    # back to the normal stdout
    print "Back to normal stdout again"
    

    最後に、一時フォルダーを作成し、コンテキストを離れるときにそれをクリーンアップする別の例を示します。

    from tempfile import mkdtemp
    from shutil import rmtree
    
    @contextmanager
    def temporary_dir(*args, **kwds):
        name = mkdtemp(*args, **kwds)
        try:
            yield name
        finally:
            shutil.rmtree(name)
    
    with temporary_dir() as dirname:
        # do whatever you want
    
于 2010-06-10T08:51:37.500 に答える
99

2 つの興味深い講義を提案します。

  • PEP 343 "with" ステートメント
  • Effbot Python の "with" ステートメントを理解する

1.ステートメントは 、withコンテキスト マネージャーによって定義されたメソッドでブロックの実行をラップするために使用されます。これにより、一般的なtry...except...finally使用パターンをカプセル化して再利用しやすくすることができます。

2. 次のようなことができます。

with open("foo.txt") as foo_file:
    data = foo_file.read()

また

from contextlib import nested
with nested(A(), B(), C()) as (X, Y, Z):
   do_something()

または (Python 3.1)

with open('data') as input_file, open('result', 'w') as output_file:
   for line in input_file:
     output_file.write(parse(line))

また

lock = threading.Lock()
with lock:
    # Critical section of code

3. ここにアンチパターンはありません。Dive into Python の
引用:

試してみてください..最終的には良いです。とのほうが良いです。

4.try..catch..finallyプログラマーが他の言語のステートメント を使用する習慣に関係していると思います。

于 2010-06-10T07:49:10.790 に答える
44

PythonwithステートメントはResource Acquisition Is Initialization、C++ で一般的に使用されるイディオムの組み込み言語サポートです。これは、オペレーティング システム リソースを安全に取得および解放できるようにすることを目的としています。

このwithステートメントは、スコープ/ブロック内にリソースを作成します。ブロック内のリソースを使用してコードを記述します。ブロックが終了すると、ブロック内のコードの結果 (つまり、ブロックが正常に終了したか、例外が発生したか) に関係なく、リソースは完全に解放されます。

ステートメントに必要なプロトコルに従う Python ライブラリの多くのリソースは、withそのまま使用できます。ただし、十分に文書化されたプロトコルを実装することにより、 with ステートメントで使用できるリソースを誰でも作成できます: PEP 0343

ファイル、ネットワーク接続、ロックなど、明示的に放棄する必要があるアプリケーション内のリソースを取得するたびに使用します。

于 2010-06-10T09:53:05.713 に答える
28

with完全を期すために、ステートメントの最も有用なユースケースを追加します。

私は多くの科学計算を行っており、いくつかの活動でDecimalは任意精度計算用のライブラリが必要です。コードの一部には高い精度が必要ですが、他のほとんどの部分では精度が低くてもかまいません。

デフォルトの精度を低い数値に設定してから、withいくつかのセクションでより正確な回答を得るために使用します。

from decimal import localcontext

with localcontext() as ctx:
    ctx.prec = 42   # Perform a high precision calculation
    s = calculate_something()
s = +s  # Round the final result back to the default precision

私はこれを超幾何学的検定でよく使用します。超幾何学的検定では、多数の結果の階乗を求める必要があります。ゲノム スケールの計算を行う場合、丸め誤差とオーバーフロー誤差に注意する必要があります。

于 2010-06-10T17:22:32.117 に答える
28

アンチパターンの例としては、ループの外側を使用withする方が効率的な場合に、ループの内側を使用することがwithあります。

例えば

for row in lines:
    with open("outfile","a") as f:
        f.write(row)

with open("outfile","a") as f:
    for row in lines:
        f.write(row)

最初の方法は、rowファイルを一度だけ開いて閉じる 2 番目の方法と比較して、パフォーマンスの問題を引き起こす可能性があるファイルをそれぞれ開いて閉じることです。

于 2010-06-10T10:25:19.500 に答える
10

PEP 343 - The 'with' statement を参照してください。最後に例のセクションがあります。

... Python 言語への新しいステートメント "with" により、try/finally ステートメントの標準的な使用法を除外することができます。

于 2010-06-10T07:45:21.527 に答える
5

ポイント 1、2、および 3 は適切にカバーされています。

4:from __future__ import with_statement比較的新しいもので、python2.6+ (または を使用した python2.5 )でのみ利用可能です。

于 2010-06-10T08:34:42.703 に答える
4

with ステートメントは、いわゆるコンテキスト マネージャーで動作します。

http://docs.python.org/release/2.5.2/lib/typecontextmanager.html

アイデアは、「with」ブロックを離れた後に必要なクリーンアップを行うことにより、例外処理を簡素化することです。Python 組み込みの一部は、既にコンテキスト マネージャーとして機能しています。

于 2010-06-10T07:45:15.537 に答える