'with'语句中有多个变量?

puf*_*ish 347 python with-statement

是否可以使用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)
Run Code Online (Sandbox Code Playgroud)

......还是正在清理两个资源同时出现问题?

Raf*_*ird 607

从v3.1Python 2.7 开始,它可以在Python 3中实现.新with语法支持多个上下文管理器:

with A() as a, B() as b, C() as c:
    doSomething(a,b,c)
Run Code Online (Sandbox Code Playgroud)

不同的是contextlib.nested,这保证了ab将他们__exit__()的调用,即使C()或者它的__enter__()方法抛出一个异常.

  • 此外,有可能:A()为a,B(a)为b,C(a,b)为c: (13认同)
  • 请注意,`as` 是可选的。 (7认同)
  • 澄清@SławomirLenart的意思:如果您需要对象“a”或“b”,则需要“as”,但不需要整个“as a”或“as b” (4认同)
  • 是否可以将字段设置为等于 with 语句中的某些内容,如`with open('./file') as arg.x = file:`? (2认同)

Ale*_*lli 56

contextlib.nested 支持这个:

import contextlib

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

   ...
Run Code Online (Sandbox Code Playgroud)

更新:
引用文档,关于contextlib.nested:

从版本2.7开始不推荐使用:with语句现在直接支持此功能(没有令人困惑的容易出错的怪癖).

有关更多信息,请参阅RafałFowgird的答案.

  • 我很遗憾地说,但我认为`嵌套`上下文管理器是一个错误,永远不应该使用.在此示例中,如果打开第二个文件引发异常,则第一个文件根本不会关闭,从而完全破坏了使用上下文管理器的目的. (33认同)
  • @James:不,http://docs.python.org/library/contextlib.html#contextlib.nested文档中的等效代码与标准嵌套`with`块不同.在输入with块之前按顺序创建管理器:m1,m2,m3 = A(),B(),C()如果B()或C()失败但有异常,那么你唯一希望正确完成A()是垃圾收集器. (10认同)
  • [自2.7版本后不推荐](http://docs.python.org/2/library/contextlib.html#contextlib.nested).注意:with语句现在直接支持此功能(没有容易出错的容易出错的怪癖). (8认同)

jim*_*qaz 29

请注意,如果将变量拆分为行,则必须使用反斜杠来换行换行符.

with A() as a, \
     B() as b, \
     C() as c:
    doSomething(a,b,c)
Run Code Online (Sandbox Code Playgroud)

括号不起作用,因为Python会创建一个元组.

with (A(),
      B(),
      C()):
    doSomething(a,b,c)
Run Code Online (Sandbox Code Playgroud)

由于元组缺少__enter__属性,因此会出现错误(无法识别并且不识别类类型):

AttributeError: __enter__
Run Code Online (Sandbox Code Playgroud)


And*_*are 18

我想你想这样做:

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)
Run Code Online (Sandbox Code Playgroud)

  • 诚然,这并不是什么大不了的事,但是,根据"导入这个"(又名"Python的禅"),"扁平比嵌套好" - 这就是我们将contextlib.nested添加到标准库的原因.顺便说一句,3.1可能有一个新的语法"用A()作为a,B()作为b:"(补丁是,到目前为止没有关于它的BDFL声明),以获得更直接的支持(显然,库解决方案不是'被认为是完美的......但是避免不必要的嵌套绝对是核心Python开发人员共同的目标. (7认同)
  • 这就是我目前的做法,但是嵌套的深度是我想要的两倍(意思是)它...... (5认同)
  • @Andrew:我认为一个级别的缩进更好地表达了程序的预期逻辑,即"原子地"创建两个变量,并在以后一起清理它们(我意识到这实际上并不是发生了什么).认为例外问题是一个交易破坏者 (4认同)
  • @Alex:非常正确但我们还必须考虑"可读性计数". (2认同)

tim*_*geb 9

因为Python 3.3,你可以使用类ExitStackcontextlib模块.

它可以管理动态数量的上下文感知对象,这意味着如果您不知道要处理多少文件,它将特别有用.

文档中提到的规范用例是管理动态数量的文件.

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
Run Code Online (Sandbox Code Playgroud)

这是一个通用的例子:

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)
Run Code Online (Sandbox Code Playgroud)

输出:

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]
Run Code Online (Sandbox Code Playgroud)


Chr*_*nds 7

从 Python 3.10 开始,有一个新功能括号上下文管理器,它允许以下语法:

with (
    A() as a,
    B() as b
):
    do_something(a, b)
Run Code Online (Sandbox Code Playgroud)

  • 爱它!最后!(到 2030 年左右我就可以在 debian 上使用它了。) (4认同)