Python多行嵌套上下文管理器

34

在Python 2.6中,我们通常这样格式化嵌套的上下文管理器:

with nested(
    context1,
    context2
) as a, b:
    pass
自Python 2.7开始,nested已经过时。我看到很多例子都是将多个上下文管理器放在一行上,但是我找不到允许它们跨多行的语法。你会如何做到这一点?
# That's working fine
with context1 as a, context2 as b:
    pass

# But how do we make it multine?
# These are not working
with (
    context1,
    context2
) as a, b:
    pass

with context1 as a,
    context2 as b:
    pass

1
不确定为什么这被标记为重复内容。其他帖子没有回答问题。我同意Python应该提出一种基于括号的语法,就像if语句一样。 - Conchylicultor
4个回答

35

Python 3.10及以上版本

从Python 3.10开始,圆括号是允许的,你终于可以这样做了:

with (
    context1 as a,
    context2 as b
):
    pass

反斜杠字符

使用反斜杠字符 (\) 可以将两个或多个物理行连接成逻辑行。

(引用 显式行连接 部分)

如果您想将上下文管理器放在不同的行上,可以通过在行末添加反斜杠来实现:

with context1 as a,\
     context2 as b:
    pass

contextlib.ExitStack

contextlib.ExitStack是一个上下文管理器,旨在使编程人员容易地组合其他上下文管理器和清理函数,特别是那些是可选的或由输入数据驱动的。

它在Python 3.3及更高版本中可用,并允许轻松进入可变数量的上下文管理器。对于仅有两个上下文管理器的情况,使用方法如下:

from contextlib import ExitStack

with ExitStack() as es:
    a = es.enter_context(context1)
    b = es.enter_context(context2)

嵌套

可以将上下文表达式分割成几个嵌套的with语句:

With more than one item, the context managers are processed as if multiple with statements were nested:

with A() as a, B() as b:

suite is equivalent to

with A() as a:
    with B() as b:
        suite

(来自 with语句)


10
这是唯一的语法吗?因为它非常丑陋... :( - Simon Boudrias
3
难以置信我要这么说,但在这种情况下,与Lisp相比,Python语法很糟糕。在(let)或其他绑定形式中进行多个赋值看起来非常自然...但这样很笨拙。 - Joseph Garvin
如果B()需要参数a,有没有办法将其传递进去? - CMCDragonkai

14

有一种创造性地使用括号并避免反斜杠的方法:在as之前给表达式加上括号。虽然不太美观:

with (
  open('/etc/passwd')) as foo, (
  open('/tmp/bar')) as bar:
  pass  # this parses correctly

如果需要,嵌套更多内容也很容易。


这是最漂亮的解决方案。谢谢! - tebanep
3
为什么在这种情况下你会想要避免使用反斜杠呢?开括号在这里起着基本相同的作用,但它也变得更加晦涩难懂。 - isarandi
2
@isarandi:在编辑时,反斜杠作为行继续符比其他一些东西更容易被打断。PEP-8建议使用括号代替反斜杠续行。顺便说一句,同样的PEP-8建议在多个“with”语句中使用“\”。我个人更喜欢嵌套的“with”,但为了完整性而添加了这个答案。 - 9000

2
with context1 as a, \
context2 as b:
pass

和任何换行符一样,反斜杠提供了解决方案


2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接