with'语句中的多个变量?

621 投票
8 回答
220318 浏览
提问于 2025-04-15 11:44

在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)

...或者说,同时处理两个资源会有问题吗?

8 个回答

66

contextlib.nested 这个东西可以支持这样的用法:

import contextlib

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

   ...

更新:
根据文档的说法,关于 contextlib.nested

自2.7版本起已不再推荐使用:现在的with语句直接支持这个功能(而且没有那些让人困惑且容易出错的怪癖)。

想了解更多信息,可以查看 Rafał Dowgird的回答

93

注意,如果你在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: invalid syntax

这个问题什么时候会解决?

这个问题正在跟踪中,具体可以查看 https://bugs.python.org/issue12782

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

新的解析器可以正确解析被括号包围的多个上下文管理器。这个新解析器在3.9版本中已经启用。有人报告说,直到Python 3.10移除旧解析器之前,这种语法仍然会被拒绝,而这个语法的变化在 3.10版本更新说明中有提到。不过在我的测试中,它在trinket.io的Python 3.9.6中也是可以正常工作的。

994

在Python 3.1版本及以上和Python 2.7中,可以使用一种新的写法。这个新的with语法支持同时管理多个上下文。

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

跟之前的contextlib.nested不同,这种写法保证了即使在执行C()或者它的__enter__()方法时出现了错误,ab__exit__()方法也会被调用。

你还可以在后面的定义中使用之前定义的变量(感谢下面的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)

撰写回答