web-dev-qa-db-ja.com

python "with"ステートメントは何のために設計されていますか?

今日初めてPython withステートメントに出会いました。私は数か月間Pythonを軽く使ってきましたが、その存在すら知りませんでした!そのやや不明瞭な状態を考えると、私はそれを尋ねる価値があると思いました:

  1. Python withステートメントは何のために使用するように設計されていますか?
  2. これを何に使いますか?
  3. 知っておく必要がある落とし穴、またはその使用に関連する一般的なアンチパターンはありますか? withよりもtry..finallyを使用した方がよい場合はありますか?
  4. なぜもっと広く使われないのですか?
  5. どの標準ライブラリクラスと互換性がありますか?
375
fmark
  1. これは私の前に他のユーザーによって既に回答されていると思うので、完全を期すためにのみ追加します:withステートメントは、いわゆる context managers 。詳細は PEP 34 にあります。たとえば、openステートメントはそれ自体がコンテキストマネージャーです。これにより、ファイルを開き、実行が使用したwithステートメントのコンテキスト内にある限り開いたままにし、ファイルを閉じたらすぐに閉じます。例外のために、または通常の制御フローの間にそれを残したかどうかに関係なく、コンテキスト。したがって、withステートメントは、C++の RAIIパターン と同様の方法で使用できます。一部のリソースはwithステートメントによって取得され、withコンテキストを終了すると解放されます。

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

    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.stdinsys.stdout、およびsys.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
    
361
Tamás

2つの興味深い講義をお勧めします。

  • PEP 34 「with」ステートメント
  • Effbot Pythonの「with」ステートメントを理解する

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

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

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

OR

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))

OR

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

3。ここにはアンチパターンが表示されません。
引用 Pythonに飛び込む

try..finallyは良いです。 withの方が良い

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

85
systempuntoout

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

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

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

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

36
Tendayi Mawushe

アンチパターンの例としては、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の方法と比較して、パフォーマンスの問題を引き起こす可能性があります。

25
John La Rooy

ここでも完全を期すために、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

私はこれを、階乗から生じる大きな数の除算を必要とする超幾何テストで頻繁に使用します。ゲノム規模の計算を行うときは、丸め誤差とオーバーフロー誤差に注意する必要があります。

24
JudoWill

PEP 343-'with'ステートメント を参照してください。末尾にサンプルセクションがあります。

... Python言語に「with」という新しいステートメントを追加し、try/finallyステートメントの標準的な使用を除外できるようにしました。

9
stefanB

ポイント1、2、および3が十分にカバーされている:

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

4
cobbal

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

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

「with」ブロックを終了した後に必要なクリーンアップを行うことにより、例外処理を簡素化するという考え方です。いくつかのpythonビルトインは既にコンテキストマネージャーとして機能しています。

3
zefciu

pythonでは通常、「with」ステートメントを使用して、ファイルを開き、ファイルに存在するデータを処理し、close()メソッドを呼び出さずにファイルを閉じます。 「with」ステートメントは、クリーンアップアクティビティを提供することにより、例外処理を簡単にします。

Withの一般的な形式:

with open(“file name”, “mode”) as file-var:
    processing statements

注: file-var.close()でclose()を呼び出してファイルを閉じる必要はありません

2
Tushar.PUCSD

すぐに使えるサポートの別の例、および組み込みのopen()の動作に慣れると最初は少し困惑するかもしれないものは、次のような一般的なデータベースモジュールのconnectionオブジェクトです。

connectionオブジェクトはコンテキストマネージャであるため、with-statementですぐに使用できますが、上記の注意事項を使用する場合は次の点に注意してください。

with-blockが、例外ありまたはなしで終了すると、接続は閉じられませんwith-blockが例外で終了した場合、トランザクションはロールバックされます。それ以外の場合、トランザクションはコミットされます。

これは、プログラマーが自分で接続を閉じるように注意する必要があることを意味しますが、 psycopg2 docs に示すように、接続を取得し、複数のwith-statementsで使用することができます。

conn = psycopg2.connect(DSN)

with conn:
    with conn.cursor() as curs:
        curs.execute(SQL1)

with conn:
    with conn.cursor() as curs:
        curs.execute(SQL2)

conn.close()

上記の例では、psycopg2cursorオブジェクトもコンテキストマネージャーであることに注意してください。動作に関する関連ドキュメントから:

cursorwith-blockを終了すると、それは閉じられ、最終的にそれに関連付けられているリソースを解放します。トランザクションの状態は影響を受けません。

2
bgse