是否可以在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)
... 还是同时清理两个资源才是问题所在?
是否可以在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)
... 还是同时清理两个资源才是问题所在?
当前回答
请注意,如果将变量拆分为行,在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。
其他回答
我认为你应该这样做:
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)
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的回答。
你也可以将创建上下文管理器(__init__方法)和进入上下文(__enter__方法)分开来增加可读性。所以不用写这段代码:
with Company(name, id) as company, Person(name, age, gender) as person, Vehicle(brand) as vehicle:
pass
你可以这样写:
company = Company(name, id)
person = Person(name, age, gender)
vehicle = Vehicle(brand)
with company, person, vehicle:
pass
注意,在with语句之外创建上下文管理器会给人一种印象,即创建的对象也可以在语句之外进一步使用。如果上下文管理器不是这样,那么错误的印象可能与可读性尝试相对应。
文件说:
大多数上下文管理器的编写方式意味着它们只能在with语句中有效地使用一次。这些单用途上下文管理器每次使用时都必须重新创建—尝试第二次使用它们将触发异常或无法正常工作。 这种常见的限制意味着,通常建议直接在with语句的头中创建上下文管理器。
在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)
从Python 3.10开始,有一个括号上下文管理器的新特性,它允许以下语法:
with (
A() as a,
B() as b
):
do_something(a, b)