是否可以在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)

... 还是同时清理两个资源才是问题所在?


当前回答

我认为你应该这样做:

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)

其他回答

这在Python 3 v3.1和Python 2.7中是可能的。新的with语法支持多个上下文管理器:

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

不像contextlib。嵌套的,这保证了a和b的__exit__()将被调用,即使C()或它的__enter__()方法引发异常。

你也可以在后面的定义中使用之前的变量(下面是h/t Ahmad):

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

从Python 3.10开始,你可以使用括号:

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

请注意,如果将变量拆分为行,在Python 3.10之前,必须使用反斜杠来换行。

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中跟踪。

Python在PEP 617中宣布,他们将用一个新的解析器取代原来的解析器。因为Python的原始解析器是LL(1),它无法区分带有(A(), B()):的“多个上下文管理器”和带有(A(), B())的“值元组”[0]:。

新的解析器可以正确地解析括号括起来的多个上下文管理器。新的解析器已在3.9中启用。据报道,这个语法仍然会被拒绝,直到旧的解析器在Python 3.10中被删除,这个语法变化在3.10发布说明中被报告。但在我的测试中,它适用于饰品。io的Python 3.9.6。

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现在支持此功能 直接使用功能(没有容易出错的令人困惑的怪癖)。

更多信息请参见rafawarsaw Dowgird的回答。

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

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

在Python 3.1+中,你可以指定多个上下文表达式,它们将被处理,就像多个with语句嵌套一样:

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

等于

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

这也意味着你可以在第二个表达式中使用第一个表达式的别名(在使用db连接/游标时很有用):

with get_conn() as conn, conn.cursor() as cursor:
    cursor.execute(sql)