【问题标题】:Create a "with" block on several context managers? [duplicate]在多个上下文管理器上创建一个“with”块? [复制]
【发布时间】:2011-03-02 18:45:25
【问题描述】:

假设您通过上下文管理器获取了三个对象,例如一个锁、一个数据库连接和一个 IP 套接字。 您可以通过以下方式获得它们:

with lock:
   with db_con:
       with socket:
            #do stuff

但是有没有办法在一个块中做到这一点?像

with lock,db_con,socket:
   #do stuff

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

a=[lock1, lock2, lock3, db_con1, socket, db_con2]
with a as res:
    #now all objects in array are acquired

如果答案是“否”,是因为需要这样的功能意味着糟糕的设计,还是我应该鼓励一下? :-P

【问题讨论】:

标签: python with-statement contextmanager


【解决方案1】:

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

with A() as X, B() as Y, C() as Z:
    do_something()

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


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

with ExitStack() as stack:
    for mgr in ctx_managers:
        stack.enter_context(mgr)
    # ...

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

contextlib2 为 Python 2.6 和 2.7 提供 a backport of ExitStack


Python 2.6及以下中,可以使用contextlib.nested

from contextlib import nested

with nested(A(), B(), C()) as (X, Y, Z):
    do_something()

相当于:

m1, m2, m3 = A(), B(), C()
with m1 as X:
    with m2 as Y:
        with m3 as Z:
            do_something()

请注意,这与通常使用嵌套的with 并不完全相同,因为A()B()C() 在进入上下文管理器之前都会首先被调用。如果这些函数之一引发异常,这将无法正常工作。

contextlib.nested 在较新的 Python 版本中已弃用,而支持上述方法。

【讨论】:

  • @noam: 不,事实上 3.1 中 nested 的文档字符串说:“这个函数相对于 with 语句的多管理器形式的一个优点是参数解包允许使用它具有可变数量的上下文管理器,如下所示:with nested(*managers): do_something()"
  • 奇怪,一方面它已被弃用,但另一方面他们承认弃用模块优于替代模块?
  • 但这通常没有区别。如果B()C() 引发异常,则会产生巨大 的不同。嵌套的with 语句将调用A().__exit__(),但contextlib.nested() 设置不会!
  • 一个问题:使用简单的“with open(A) as a, open(B) as b:”样式语法,换行符使标准工具报告的 pep8 合规性看似不可能。我使用反斜杠来表示换行符,因为用括号将逗号分隔的表达式括起来会导致报告语法错误。使用反斜杠,我收到“E127 续行过度缩进”警告。我还没有找到一种在抑制所有警告的同时使用这种语法的方法。
  • @DarrenRinger 我遇到了同样的问题。我也使用了我讨厌的反斜杠来实现这​​一点。在第一个 with 行之后将所有上下文管理器双缩进。只有单个缩进被包装的内容。它为我传递了 flake8。
【解决方案2】:

更新:从 python 3.10 开始,您将能够使用 parenthesized context managers!谢谢@iforapsy!

with (
    mock.patch('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa') as a,
    mock.patch('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb') as b,
    mock.patch('cccccccccccccccccccccccccccccccccccccccccc') as c,
):
    do_something()

原答案对于python版本

@interjay 的回答是正确的。但是,如果您需要为长上下文管理器(例如 mock.patch 上下文管理器)执行此操作,那么您很快就会意识到您想要跨行打破这一点。事实证明你不能用括号把它们包起来,所以你必须使用反斜杠。看起来是这样的:

with mock.patch('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa') as a, \
        mock.patch('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb') as b, \
        mock.patch('cccccccccccccccccccccccccccccccccccccccccc') as c:
    do_something()

【讨论】:

  • 丑陋的。为什么不允许用括号括起来(我被这个咬了)
  • @JohnDeighan 我同意,这很丑。我也被它咬了,这就是我发布这个的原因。我希望这会有所帮助,尽管我同意,但我希望它支持括号包装。
  • @JohnDeighan 为什么使用 \ 的换行符比使用括号更难看?
  • 为避免使用反斜杠,您可以将其格式化为like this(必须将其链接为图像,因为 cmets 不允许换行,此问题已关闭以获取更多答案)。
  • mock.patch 应该评估为一个对象。我们也可以写a_ctx = mock.patch('a'); ...; with a_ctx as a, ...:
【解决方案3】:

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

如果有多个项目,上下文管理器的处理方式就像嵌套了多个 with 语句:

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

等价于

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

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

【讨论】:

  • 谢谢!但这仍然没有回答我的整个问题:我提到的第二种情况呢,上下文管理器在数组中给出,而不知道数组中有多少管理器。是否有可能在某些 python3.X 中做with [cm1,cm2,cm3,cm4,cm5] as result: ....
  • @noam:要解决问题的第二部分,您可以编写一个类来包装大量资源并为该类实现__enter____exit__。我不确定是否有一个标准库类已经这样做了。
  • @Mark 我不认为这很容易 - 这就是 contextlib.nested() 被弃用的原因。如果在其他事物的生成和上下文管理器的激活之间发生了某些事情,则可能会发生清理未按预期进行的情况。
【解决方案4】:

您的问题的第二部分通过contextlib.ExitStack in Python 3.3 解决。

【讨论】:

    【解决方案5】:

    根据@sage88 的回复,您始终可以在输入这些补丁之前为其分配有意义的变量名称。

    您可以在多行中创建这些补丁

    a_patch = mock.patch('aaaaaaa') 
    b_patch = mock.patch('bbbbbbb')
    c_patch = mock.patch('ccccccc') 
    with a_patch as a, b_patch as b, c_patch as c:    
        do_something()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-03
      • 1970-01-01
      • 1970-01-01
      • 2013-01-04
      • 2019-05-30
      • 2014-01-24
      • 1970-01-01
      • 2021-05-09
      相关资源
      最近更新 更多