with
今日初めてPython ステートメントに出会いました。数か月間 Python を軽く使っていましたが、その存在すら知りませんでした。あまり知られていない状況なので、質問してみる価値があると思いました。
- Python
with
ステートメントは何の目的で設計されているのでしょうか? - あなたはそれを何に使っているの?
- 知っておく必要のある落とし穴や、その使用に関連する一般的なアンチパターンはありますか?
try..finally
よりもそれを使用する方が良いケースはありますかwith
? - なぜもっと広く使われないのでしょうか?
- どの標準ライブラリクラスが互換性がありますか?
ベストアンサー1
これはすでに他のユーザーが回答していると思うので、完全性のために追加します。この
with
ステートメントは、一般的な準備とクリーンアップタスクをいわゆるコンテキストマネージャー詳細は以下をご覧ください。ペップ343たとえば、open
ステートメントはそれ自体がコンテキストマネージャであり、ファイルを開き、それwith
を使用したステートメントのコンテキスト内で実行されている限り開いたままにし、例外のためまたは通常の制御フロー中にコンテキストを離れたかどうかに関係なく、コンテキストを離れるとすぐにファイルを閉じることがwith
できます。したがって、ステートメントは、RAIIパターンC++ の場合: 一部のリソースはステートメントによって取得され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