根据条件使用不同的上下文管理器

Using different context managers depending on condition

是否可以根据某些条件使用不同的上下文管理器执行单个块?

示例:

if some_condition:
    with ContextManager(**args) as contex:
        ... # some block
else:
    with OtherContextManager(**other_args) as contex:
        ... # the same block

一种方法是将 ... 包装到一个函数中,但这对我来说可能不太方便。还有其他可能吗?

您可以将构建的对象存储在变量中,例如:

if some_condition:
    cm = ContextManager(**args)
else:
    cm = OtherContextManager(**other_args)

with cm as contex:
        ... # some block

以上内容可以很容易地扩展到三个可能的上下文管理器等。例如,您还可以决定先 "patch" 上下文管理器,然后再 "entering" 上下文。

虽然经常看到像 with foo() as bar: 这样的模式,但实际上 Python 只是计算 foo(),获取该元素,然后在对象上调用 .__enter__() .该方法的结果存储在 bar.

所以foo()调用没有什么"special",你可以在左边使用任何类型的对象。因此,您可以将 if-else 逻辑封装在一个单独的函数中,并将 return 上下文管理器封装起来,然后使用变量,或将上下文管理器作为参数传递。只要你在 with 语句中使用它,Python 就会在幕后调用 .__enter__(..).__exit__(..)

怎么样...

with ContextManager(**args) if some_condition else OtherContextManager(**other_args) as contex:
    ... # some block

...?

我们可以疯狂地利用 __enter____exit__ 都只是方法并且它们在原始对象中被调用的事实(而不是 [= 返回的对象) 12=]):

class WrapperContext:

 def __init__(self, condition):
     if condition:
         self.real_context = ContextA()
     else:
         self.real_context = ContextB()

 def __enter__(self):
     return self.real_context.__enter__()

 def __exit__(self):
     return self.real_context.__exit__()

并像这样使用它:

 with WrapperContext(condition) as obj: