在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
\
s 的位置也没有任何明显的变化。
有没有一种简洁的方法来创建允许在其中添加注释的多行 with
语句?
最佳答案
As of Python 3.10 ,现在可以将整个上下文管理器组括起来,就像您最初尝试的那样:
with (Dummy() as a, Dummy() as b,
# comment about c
Dummy() as c):
pass
这在 3.9 中在技术上也是可行的,但处于一种半文档化的边缘。
一方面,它在 3.10 中被记录为新功能,3.9 不不应该引入任何依赖于 new parser implementation 的功能(例如这个) ,以及 3.9 with
docs禁止这种形式。另一方面,该功能最终在 3.9 CPython 实现中被激活,并且(大部分?)自动生成的 3.9 full grammar spec包括括号内的表格。
在以前的 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 先生在评论中提到的,有一个 contextlib backport在名称为 contextlib2
的 PyPI 上。如果您使用的是 Python 2,则可以使用 backport 的 ExitStack
实现。
顺便说一句,你不能这样做的原因
with (
ThingA() as a,
ThingB() as b):
...
在新的解析器实现之前是因为 (
也可以是上下文管理器表达式的第一个标记,而 CPython 的旧解析器将无法判断它应该是什么规则当它看到第一个 (
时进行解析。这是 PEP 617 的新的基于 PEG 的解析器的激励示例之一。
关于Python 多行 with 语句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31039022/