1. ホーム
  2. python

[解決済み] Pythonの "with "文は何のために作られたのですか?

2022-03-20 15:10:54

質問

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

  1. Pythonとは with ステートメント に使用されるように設計されていますか?
  2. 何を に使用するのですか?
  3. を使用します。 注意すべき点や よくあるアンチパターン を使用することができますか? 使用した方が良いケースはありますか? try..finally よりも with ?
  4. なぜもっと広く使われていないのでしょうか?
  5. 標準ライブラリのどのクラスと互換性がありますか?

どのように解決するのですか?

  1. これは、私より前に他のユーザーがすでに回答していると思いますので、念のため補足します。 with ステートメントを使用すると、一般的な準備と後始末のタスクをカプセル化して、例外処理を簡素化できます。 コンテキストマネージャ . 詳細は PEP 343 . 例えば open 文はそれ自体がコンテキストマネージャであり、ファイルを開くことができ、その実行が with そして、例外の発生や通常の制御フローに関係なく、 コンテキストを離れるとすぐに閉じます。このとき with 文は、以下のような使い方ができます。 RAIIパターン C++では、あるリソースは with ステートメントから離れると解放されます。 with のコンテキストを使用します。

  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