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

时间:2010-06-11 17:39:04

标签: python with-statement contextmanager

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

5 个答案:

答案 0 :(得分:281)

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版本中已弃用,支持上述方法。

答案 1 :(得分:22)

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

  

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

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

相当于

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

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

答案 2 :(得分:15)

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

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

答案 3 :(得分:8)

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

答案 4 :(得分:0)

在@ 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, as c:    
    do_something()