在几个上下文管理器上创建一个"with"块?

ola*_*ndo 180 python with-statement contextmanager

假设您有三个通过上下文管理器获取的对象,例如A锁,数据库连接和ip套接字.您可以通过以下方式获取它

with lock:
   with db_con:
       with socket:
            #do stuff
Run Code Online (Sandbox Code Playgroud)

但有没有办法在一个街区内完成?就像是

with lock,db_con,socket:
   #do stuff
Run Code Online (Sandbox Code Playgroud)

此外,如果有一组具有上下文管理器的未知长度的对象,是否有可能以某种方式做到:

a=[lock1, lock2, lock3, db_con1, socket, db_con2]
with a as res:
    #now all objects in array are acquired
Run Code Online (Sandbox Code Playgroud)

如果答案是"不",是不是因为需要这样的功能意味着设计不好,或者我应该建议它?:-P

int*_*jay 319

Python 2.7和3.1及更高版本中,您可以编写:

with A() as X, B() as Y, C() as Z:
    do_something()
Run Code Online (Sandbox Code Playgroud)

这通常是最好的方法,但是如果你有一个未知长度的上下文管理器列表,你将需要以下方法之一.


Python 3.3中,您可以使用contextlib.ExitStack输入未知长度的上下文管理器列表:

with ExitStack() as stack:
    for mgr in ctx_managers:
        stack.enter_context(mgr)
    # ...
Run Code Online (Sandbox Code Playgroud)

这允许您在将上下文管理器添加到上下文管理器时创建上下文管理器ExitStack,从而防止可能出现的问题contextlib.nested(如下所述).

contextlib2为Python 2.6和2.7 提供了一个反向移植ExitStack.


Python 2.6及更低版本中,您可以使用contextlib.nested:

from contextlib import nested

with nested(A(), B(), C()) as (X, Y, Z):
    do_something()
Run Code Online (Sandbox Code Playgroud)

相当于:

m1, m2, m3 = A(), B(), C()
with m1 as X:
    with m2 as Y:
        with m3 as Z:
            do_something()
Run Code Online (Sandbox Code Playgroud)

请注意,这是不完全的正常使用嵌套相同with,因为A(),B()C()都将最初称,进入上下文经理之前.如果其中一个函数引发异常,则无法正常工作.

contextlib.nested 在较新的Python版本中不赞成使用上述方法.

  • 奇怪的是,一方面它被弃用了,但另一方面它们承认弃用模块优于替换模块的优势? (9认同)
  • *但通常这没有区别*.如果`B()`或`C()`引发异常,它会产生巨大的**差异.嵌套的`with`语句将调用`A().__ exit __()`,但`contextlib.nested()`设置不会! (6认同)
  • 一个问题:使用简单的"with open(A)as a,open(B)as b:"样式语法,换行使pep8符合标准工具所报告的似乎是不可能的.我使用反斜杠来表示换行符,因为用括号括起逗号分隔的表达式会导致报告语法错误.使用反斜杠,我得到一个"E127延续线过度缩进"警告.我还没有找到一种方法来使用这种语法,同时抑制所有警告. (5认同)
  • @DarrenRinger我有同样的问题.我也使用反斜杠来实现这​​个目标.在初始化后用行缩进所有上下文管理器.只有单个缩进包含的内容.它为我传递了flake8. (4认同)
  • @noam:不,实际上3.1中`嵌套`的文档字符串说:"这个函数优于with语句的多个管理器形式的一个优点是参数解包允许它与可变数量的上下文管理器一起使用如下:`与嵌套(*管理者):do_something()`" (3认同)

Mar*_*ers 25

您可以在Python 3.1中提出问题的第一部分.

如果有多个项目,则会处理上下文管理器,就好像多个with语句嵌套一样:

with A() as a, B() as b:
    suite
Run Code Online (Sandbox Code Playgroud)

相当于

with A() as a:
    with B() as b:
        suite
Run Code Online (Sandbox Code Playgroud)

在3.1版中更改:支持多个上下文表达式

  • @noam:要解决问题的第二部分,你可以编写一个类来包装大量资源,并为该类实现`__enter__`和`__exit__`.我不确定是否有一个标准库类已经这样做了. (2认同)

sag*_*e88 18

@ interjay的答案是对的.但是,如果您需要为长期上下文管理器(例如mock.patch上下文管理器)执行此操作,那么您很快就会意识到要跨行分解.事实证明你不能将它们包裹在parens中,所以你必须使用反斜杠.这是看起来像:

with mock.patch('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa') as a, \
        mock.patch('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb') as b, \
        mock.patch('cccccccccccccccccccccccccccccccccccccccccc') as c:
    do_something()
Run Code Online (Sandbox Code Playgroud)

  • 丑陋的。为什么不允许用括号括起来(我对此感到不满) (3认同)
  • 好消息,伙计们。带括号的上下文管理器将是有效的语法[从 Python 3.10 开始,得益于新的解析器](https://docs.python.org/3.10/whatsnew/3.10.html#parenthesized-context-managers)。 (3认同)
  • 为了避免反斜杠,您可以将其格式化[像这样](https://i.stack.imgur.com/KzuwQ.png)(必须将其链接为图像,因为注释不允许换行,并且这个问题是已关闭以获得更多答案)。 (2认同)
  • mock.patch 应该评估为一个对象。我们还可以写 `a_ctx = mock.patch('a'); ...; 将 a_ctx 作为 a, ...:` (2认同)

Nei*_*l G 9

你问题的第二部分是解决了contextlib.ExitStackPython的3.3.