在 python 中创建多行
with
的干净方法是什么?我想在一个 with
中打开多个文件,但它离右侧足够远,我希望它在多行上。像这样:
class Dummy:
def __enter__(self): pass
def __exit__(self, type, value, traceback): pass
with Dummy() as a, Dummy() as b,
Dummy() as c:
pass
不幸的是,这是一个
SyntaxError
。所以我尝试了这个:
with (Dummy() as a, Dummy() as b,
Dummy() as c):
pass
还有一个语法错误。然而,这有效:
with Dummy() as a, Dummy() as b,\
Dummy() as c:
pass
但是如果我想发表评论怎么办?这不起作用:
with Dummy() as a, Dummy() as b,\
# my comment explaining why I wanted Dummy() as c\
Dummy() as c:
pass
\
的位置也没有任何明显的变化。
是否有一种干净的方法来创建多行
with
语句,并允许在其中添加注释?
从 Python 3.10 开始,现在可以将整个上下文管理器组括起来,就像您最初尝试的那样:
with (Dummy() as a, Dummy() as b,
# comment about c
Dummy() as c):
pass
这在 3.9 中在技术上也是可能的,但处于一种半文档化的状态。
一方面,它在 3.10 中被记录为新的,3.9 并没有应该引入任何依赖于新解析器实现的功能(像这个),并且3.9
with
文档禁止这种形式。另一方面,该功能最终在 3.9 CPython 实现中被激活,并且(大部分?)自动生成的 3.9 完整语法规范 包括括号形式。
在以前的 Python 3 版本中,如果您需要在上下文管理器中散布注释,我会使用
contextlib.ExitStack
:
from contextlib import ExitStack
with ExitStack() as stack:
a = stack.enter_context(Dummy()) # Relevant comment
b = stack.enter_context(Dummy()) # Comment about b
c = stack.enter_context(Dummy()) # Further information
这相当于
with Dummy() as a, Dummy() as b, Dummy() as c:
这样做的好处是您可以在循环中生成上下文管理器,而无需单独列出每个上下文管理器。该文档给出了示例,如果您想打开一堆文件,并且文件名在列表中,您可以这样做
with ExitStack() as stack:
files = [stack.enter_context(open(fname)) for fname in filenames]
如果你的上下文管理器占用了太多的屏幕空间,以至于你想在它们之间放置注释,那么你可能有足够的空间想要使用某种循环。
正如 Deathless 先生在评论中提到的,PyPI 上有一个名为 contextlib2
的
contextlib 向后移植。如果您使用的是 Python 2,则可以使用向后移植的
ExitStack
实现。
顺便说一下,你不能这样做的原因
with (
ThingA() as a,
ThingB() as b):
...
在新的解析器实现之前是因为
(
也可以是上下文管理器表达式的第一个标记,而 CPython 的旧解析器在看到第一个 时将无法判断它应该解析什么规则(
。这是PEP 617的新的基于 PEG 的解析器的激励示例之一。
仅限 Python 3.9+:
with (
Dummy() as a,
Dummy() as b,
# my comment explaining why I wanted Dummy() as c
Dummy() as c,
):
pass
Python ≤ 3.8:
with \
Dummy() as a, \
Dummy() as b, \
Dummy() as c:
pass
不幸的是,这种语法无法进行注释。
这对我来说似乎最整洁:
with open('firstfile', 'r') as (f1 # first
), open('secondfile', 'r') as (f2 # second
):
pass
这并不完全干净,但你可以这样做:
with Dummy() as a, Dummy() as b, (
#my comment
Dummy()) as c:
pass
没有语法错误,但它不是最干净的。你也可以这样做:
with Dummy() as a, Dummy() as b, Dummy(
#my comment
) as c:
pass
考虑找到一种方法来做到这一点,而不使用
with
中间的注释。
我会通过在
with
语句之前或行本身添加注释来使事情简单易读:
# my comment explaining why I wanted Dummy() as c
with Dummy() as a, Dummy() as b,\
Dummy() as c: # or add the comment here
pass
像TigerhawkT3的答案一样,但是缩进不会触发pycodestyle的错误E124:
with (
open('firstfile', 'r')) as f1, ( # first
open('secondfile', 'r')) as f2: # second
pass
IMO 它仍然很丑,但至少它通过了 linter。
为了避免排太长的队伍,您可以执行以下操作。
代替:
with open(filename, 'wb') as file, request.urlopen(image.url) as response:
pass
我,例如:
f = lambda: open(filename, 'wb')
r = lambda: request.urlopen(image.url)
with r() as response, f() as file:
pass