在多个上下文管理器上创建“with”块?

2024-05-23 19:57:03 发布

您现在位置:Python中文网/ 问答频道 /正文

假设您有三个通过上下文管理器获取的对象,例如一个锁、一个db连接和一个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

如果答案是“不”,是因为对这样一个功能的需求意味着糟糕的设计,还是我应该在pep中提出建议?:-P页


Tags: 对象iplock管理器dbwith方式数组
3条回答

问题的第一部分可以在Python 3.1中找到。

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

Changed in version 3.1: Support for multiple context expressions

@interjay的回答是正确的。但是,如果您需要为长上下文管理器(例如mock.patch上下文管理器)执行此操作,那么您很快就会意识到需要跨行中断此操作。原来你不能用帕伦斯来包装,所以你必须用反斜杠。下面是它的样子:

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

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 ^{}


Python 2.6及以下版本中,您可以使用^{}

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版本中被弃用,取而代之的是上述方法。

相关问题 更多 >