Python 多行 with 语句

问题描述 投票:0回答:7

在 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 python-3.x multiline with-statement
7个回答
71
投票

从 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 的解析器的激励示例之一。


18
投票

仅限 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

不幸的是,这种语法无法进行注释。


13
投票

这对我来说似乎最整洁:

with open('firstfile', 'r') as (f1 # first
  ), open('secondfile', 'r') as (f2 # second
  ):
    pass

4
投票

这并不完全干净,但你可以这样做:

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
中间的注释。


3
投票

我会通过在

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

0
投票

TigerhawkT3的答案一样,但是缩进不会触发pycodestyle的错误E124

with (
        open('firstfile', 'r')) as f1, (  # first
        open('secondfile', 'r')) as f2:  # second
    pass

IMO 它仍然很丑,但至少它通过了 linter。


0
投票

为了避免排太长的队伍,您可以执行以下操作。

代替:

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
© www.soinside.com 2019 - 2024. All rights reserved.