Python 多行嵌套上下文管理器

Python nested context manager on multiple lines

在 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

Python 3.10 及更高版本

从Python 3.10开始,parentheses are allowed,你终于可以这样做了:

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

反斜杠字符

Two or more physical lines may be joined into logical lines using backslash characters (\)

(引用 Explicit line joining 部分)

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

with context1 as a,\
     context2 as b:
    pass

contextlib.ExitStack

contextlib.ExitStack 是一个

context manager that is designed to make it easy to programmatically combine other context managers and cleanup functions, especially those that are optional or otherwise driven by input data.

它在 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

(来自 The with statement

with context1 as a, \
context2 as b:
pass

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

你的例子相当于:

with context1 as a:
    with context2 as b:
        pass

两行看起来不错。

参考:https://docs.python.org/2.7/reference/compound_stmts.html#the-with-statement

有一种方法可以创造性地使用括号并避免反斜杠:在 as 之前将表达式括起来。不过不太好看:

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

如果需要,嵌套越来越多很容易。