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


391

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

何かのようなもの:

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つのリソースをクリーンアップしていますか?


[expr1、expr2]をfとして使用し、f [0]とf [1]を使用するとします。
jbasko 2009年

何かをインポートする必要がないので良かったのですが...機能しませんAttributeError: 'list' object has no attribute ' exit '
pufferfish

Pythonはちょうどクロージャを持っていた場合は、ステートメントに必要はありません
BT

withステートメントを使用する必要ありませんよね?file_outとfile_inをNoneに設定してから、try / except / finallyを開いてtryで処理し、Noneでない場合は最終的に閉じることができます。そのために二重インデントは必要ありません。
Mカッツ

1
これらの答えの多くは、2つ以上のwithステートメントの必要性に対応していません。理論的には、数十のコンテキストを開く必要のあるアプリケーションが存在する可能性があります。ネストは、行の長さに制限が課されるとすぐにバラバラになります。
ThorSummoner 2014

回答:


667

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

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

異なりcontextlib.nested、この保証はそのabその必要があります__exit__()「と呼ばれていてもsのC()か、それの__enter__()メソッドが例外を発生させます。

以前の変数を後の定義で使用することもできます(以下のh / t Ahmad)。

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

1
のようにwithステートメントで何かに等しいフィールドを設定することは可能with open('./file') as arg.x = file:ですか?
チャーリーパーカー

13
また、次のことも可能です:A()をa、B(a)をb、C(a、b)をc:
Ahmad Yoosofan 2017

クラスtest2:x = 1; t2 = test2()with open( 'f2.txt')as t2.x:l1 for t2.x.readlines():print(l1); #チャーリーパーカー
#Python

1
asオプションであることに注意してください。
SławomirLenart

@SławomirLenartが言っていることを明確にするために:asオブジェクトaまたはが必要な場合は必須ですbが、全体as aまたはas b必須ではありません
CiprianTomoiagăMay

56

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の回答を参照してください。


34
申し訳ありませんが、nestedコンテキストマネージャは間違いであり、使用すべきではないと思います。この例では、2番目のファイルを開くと例外が発生した場合、最初のファイルはまったく閉じられないため、コンテキストマネージャを使用する目的が完全に破壊されます。
ラファウDowgird

どうしてそんなこと言うの?ドキュメントには、ネストされたを使用するのと」ネストされたと同等であることを述べている
ジェームズ・ホプキンス

@Rafal:マニュアルを見ると、Pythonがwithステートメントを適切にネストしていることがわかります。実際の問題は、2番目のファイルが閉じるときに例外をスローするかどうかです。
不明

10
@James:いいえ、でドキュメントの等価コードdocs.python.org/library/contextlib.html#contextlib.nested標準のネストされたと異なるwithブロック。マネージャーはwithブロック入るに順番作成されます:m1、m2、m3 = A()、B()、C()B()またはC()が例外で失敗した場合、A( )はガベージコレクタです。
ラファウDowgird

8
バージョン2.7以降非推奨。注:with-statementは、この機能を直接サポートするようになりました(紛らわしいエラーを起こしやすい癖なしで)。
miku

36

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

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

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

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がこの問題に関連しているようです。


16

代わりにこれを実行したいと思います。

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)

5
これが私が現在行っている方法ですが、入れ子の深さは希望の2倍です(つまり)
フグ

私はこれが最もクリーンなアプローチだと思います-他のアプローチは読みにくくなります。Alex Martelliの答えは、あなたが望んでいるものに近いように見えますが、はるかに読みにくいです。なぜそのような懸念がネストしているのですか?
Andrew Hare、

7
確かに大したことではありませんが、「これをインポートする」(「Zen of Python」とも呼ばれます)ごとに、「ネストするよりフラットの方が優れています」という理由で、標準ライブラリにcontextlib.nestedを追加しました。ちなみに、3.1には、より直接的なサポートのために、「A()をa、B()をb:として」という新しい構文が含まれる可能性があります(ただし、これまでのところ、BDFLの宣言はありません)。完璧だと考えられています...しかし、不要なネストを回避することは、Pythonのコア開発者の間で広く共有されている目標です)。
Alex Martelli、

2
@アレックス:非常に真実ですが、「読みやすさ」も考慮に入れる必要があります。
Andrew Hare、

4
@Andrew:1つのレベルのインデントは、プログラムの目的のロジックをより適切に表現すると思います。つまり、2つの変数を「原子的に」作成し、後で一緒にクリーンアップします(これは実際には何も起こらないことに気づきます)。ただし、例外的な問題は取引ブレーカーであると考えてください
フグ

12

Python 3.3以降でExitStackは、contextlibモジュールのクラスを使用できます。

動的な数のコンテキスト対応オブジェクトを管理できます。つまり、処理するファイルの数がわからない場合に特に役立ちます。

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

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]

0

Python 3.1以降では、複数のコンテキスト式を指定でき、それらは複数のwithステートメントがネストされているかのように処理されます。

with A() as a, B() as b:
    suite

に相当

with A() as a:
    with B() as b:
        suite

これは、最初の式のエイリアスを2番目の式で使用できることも意味します(db接続/カーソルを操作するときに役立ちます)。

with get_conn() as conn, conn.cursor() as cursor:
    cursor.execute(sql)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.