web-dev-qa-db-ja.com

「with」ステートメント内の複数の変数?

Pythonでwithステートメントを使用して複数の変数を宣言することは可能ですか?

何かのようなもの:

from __future__ import with_statement

with open("out.txt","wt"), open("in.txt") as file_out, file_in:
    for line in file_in:
        file_out.write(line)

...または、2つのリソースを同時にクリーンアップしていますか?

318
pufferfish

v3.1以降のPython および Python 2.7 で可能です。新しい with構文 は、複数のコンテキストマネージャーをサポートします。

with A() as a, B() as b, C() as c:
    doSomething(a,b,c)

contextlib.nestedとは異なり、これにより、__exit__()またはC()メソッドが例外を発生させた場合でも、aおよびb__enter__()が呼び出されることが保証されます。

550
Rafał Dowgird

contextlib.nested はこれをサポートします:

import contextlib

with contextlib.nested(open("out.txt","wt"), open("in.txt")) as (file_out, file_in):

   ...

更新:
contextlib.nested に関するドキュメントを引用するには:

バージョン2.7から非推奨:with-statementは、この機能を直接サポートするようになりました(紛らわしいエラーが発生しやすい癖はありません)。

詳細については、 RafałDowgirdの回答 を参照してください。

56
Alex Martelli

変数を行に分割する場合は、バックスラッシュを使用して改行をラップする必要があることに注意してください。

with A() as a, \
     B() as b, \
     C() as c:
    doSomething(a,b,c)

Pythonは代わりにTupleを作成するため、括弧は機能しません。

with (A(),
      B(),
      C()):
    doSomething(a,b,c)

タプルには__enter__属性がないため、エラーが発生します(説明的でなく、クラスタイプを識別しません)。

AttributeError: __enter__

括弧内でasを使用しようとすると、Pythonは解析時に間違いをキャッチします。

with (A() as a,
      B() as b,
      C() as c):
    doSomething(a,b,c)

SyntaxError:無効な構文

https://bugs.python.org/issue12782 はこの問題に関連しているようです。

18
jimbo1qaz

代わりにこれを行いたいと思います:

from __future__ import with_statement

with open("out.txt","wt") as file_out:
    with open("in.txt") as file_in:
        for line in file_in:
            file_out.write(line)
18
Andrew Hare

Python 3.3以降では、クラス ExitStackcontextlib モジュールから使用できます。

dynamic数のコンテキスト認識オブジェクトを管理できます。つまり、使用するファイルの数がわからない場合に特に役立つことがわかります。処理します。

ドキュメントに記載されている標準的なユースケースは、動的な数のファイルの管理です。

with ExitStack() as stack:
    files = [stack.enter_context(open(fname)) for fname in filenames]
    # All opened files will automatically be closed at the end of
    # the with statement, even if attempts to open files later
    # in the list raise an exception

一般的な例を次に示します。

from contextlib import ExitStack

class X:
    num = 1

    def __init__(self):
        self.num = X.num
        X.num += 1

    def __repr__(self):
        cls = type(self)
        return '{cls.__name__}{self.num}'.format(cls=cls, self=self)

    def __enter__(self):
        print('enter {!r}'.format(self))
        return self.num

    def __exit__(self, exc_type, exc_value, traceback):
        print('exit {!r}'.format(self))
        return True

xs = [X() for _ in range(3)]

with ExitStack() as stack:
    print(stack._exit_callbacks)
    nums = [stack.enter_context(x) for x in xs]
    print(stack._exit_callbacks)
print(stack._exit_callbacks)
print(nums)

出力:

deque([])
enter X1
enter X2
enter X3
deque([<function ExitStack._Push_cm_exit.<locals>._exit_wrapper at 0x7f5c95f86158>, <function ExitStack._Push_cm_exit.<locals>._exit_wrapper at 0x7f5c95f861e0>, <function ExitStack._Push_cm_exit.<locals>._exit_wrapper at 0x7f5c95f86268>])
exit X3
exit X2
exit X1
deque([])
[1, 2, 3]
9
timgeb