假设您通过上下文管理器获得了三个对象,例如锁、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


你问题的第一部分可以在Python 3.1中实现。

对于多个项,上下文管理器的处理就像多个With语句嵌套一样: 以A()为A, B()为B: 套件 等于 用A()作为A: 用B()作为B: 套件 在3.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可能出现的问题。嵌套(下面会提到)。

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()

等价于:

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


你的问题的第二部分是用contextlib解决的。Python 3.3中的ExitStack。


从python 3.10开始,你将能够使用括号括起来的上下文管理器!谢谢@iforapsy !

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

对于python版本< 3.10

@interjay的答案是正确的。但是,如果您需要为长上下文管理器这样做,例如mock。补丁上下文管理器,然后你很快意识到你想要跨行打破它。结果是不能用双斜杠括起来,只能用反斜杠。这是它的样子:

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

根据@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()