今日初めてPython with
ステートメントに出会いました。私は数か月間Pythonを軽く使ってきましたが、その存在すら知りませんでした!そのやや不明瞭な状態を考えると、私はそれを尋ねる価値があると思いました:
with
ステートメントは何のために使用するように設計されていますか?with
よりもtry..finally
を使用した方がよい場合はありますか?これは私の前に他のユーザーによって既に回答されていると思うので、完全を期すためにのみ追加します:with
ステートメントは、いわゆる context managers 。詳細は PEP 34 にあります。たとえば、open
ステートメントはそれ自体がコンテキストマネージャーです。これにより、ファイルを開き、実行が使用したwith
ステートメントのコンテキスト内にある限り開いたままにし、ファイルを閉じたらすぐに閉じます。例外のために、または通常の制御フローの間にそれを残したかどうかに関係なく、コンテキスト。したがって、with
ステートメントは、C++の RAIIパターン と同様の方法で使用できます。一部のリソースはwith
ステートメントによって取得され、with
コンテキストを終了すると解放されます。
例としては、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.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
2つの興味深い講義をお勧めします。
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
ステートメントを使用することはプログラマの習慣に関係していると思います。
Python with
ステートメントは、C++で一般的に使用される Resource Acquisition Is Initialization
イディオムの組み込み言語サポートです。オペレーティングシステムリソースの安全な取得と解放を可能にすることを目的としています。
with
ステートメントは、スコープ/ブロック内にリソースを作成します。ブロック内のリソースを使用してコードを記述します。ブロックが終了すると、ブロック内のコードの結果(つまり、ブロックが正常に終了するか、例外のために終了するかどうか)に関係なく、リソースは完全に解放されます。
with
ステートメントに必要なプロトコルに従うPythonライブラリ内の多くのリソースは、すぐに使用できます。ただし、十分に文書化されたプロトコルを実装することで、withステートメントで使用できるリソースを誰でも作成できます。 PEP 034
ファイル、ネットワーク接続、ロックなど、明示的に放棄する必要のあるアプリケーションのリソースを取得する場合は常に使用します。
アンチパターンの例としては、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の方法と比較して、パフォーマンスの問題を引き起こす可能性があります。
ここでも完全を期すために、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
私はこれを、階乗から生じる大きな数の除算を必要とする超幾何テストで頻繁に使用します。ゲノム規模の計算を行うときは、丸め誤差とオーバーフロー誤差に注意する必要があります。
PEP 343-'with'ステートメント を参照してください。末尾にサンプルセクションがあります。
... Python言語に「with」という新しいステートメントを追加し、try/finallyステートメントの標準的な使用を除外できるようにしました。
ポイント1、2、および3が十分にカバーされている:
4:比較的新しく、python2.6 +(またはfrom __future__ import with_statement
を使用するpython2.5)でのみ利用可能
Withステートメントは、いわゆるコンテキストマネージャーで動作します。
http://docs.python.org/release/2.5.2/lib/typecontextmanager.html
「with」ブロックを終了した後に必要なクリーンアップを行うことにより、例外処理を簡素化するという考え方です。いくつかのpythonビルトインは既にコンテキストマネージャーとして機能しています。
pythonでは通常、「with」ステートメントを使用して、ファイルを開き、ファイルに存在するデータを処理し、close()メソッドを呼び出さずにファイルを閉じます。 「with」ステートメントは、クリーンアップアクティビティを提供することにより、例外処理を簡単にします。
Withの一般的な形式:
with open(“file name”, “mode”) as file-var:
processing statements
注: file-var.close()でclose()を呼び出してファイルを閉じる必要はありません
すぐに使えるサポートの別の例、および組み込みの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()
上記の例では、psycopg2
のcursor
オブジェクトもコンテキストマネージャーであることに注意してください。動作に関する関連ドキュメントから:
cursor
がwith-block
を終了すると、それは閉じられ、最終的にそれに関連付けられているリソースを解放します。トランザクションの状態は影響を受けません。