如何将@contextmanager(包装器)用作标准函数

How to use a @contextmanager (wrapper) as a standard function

此问题与相似,但略有不同。

我有多个类,每个都定义了几个@contextmanagers:

class MyClass:
    @contextmanager
    def begin_foo(param):
        [some code]
        yield
        [some code]

    [...]

我还有一个 API 模块作为 类 的外观。 在 API 模块中,我发现我需要“包装”内部上下文管理器,以便我可以在客户端代码中使用它们:

@contextmanager
def begin_foo(param):
    with myobj.begin_foo(param):
        yield

我的客户端代码现在看起来像这样:

# Case 1: Some things need to be done in the context of foo
with begin_foo(param):
    [ stuff done in the context of foo ]

# Case 2: Nothing needs to be done in the context of foo --
# just run the entry and exit code
with begin_foo(param):
    pass

问题:在第二种情况下,有没有一种方法可以将 begin_foo 用作标准函数,而不需要 with ... pass 构造?即,只需这样做:

# Case 2: Nothing needs to be done in the context of foo --
# just run the entry and exit code
begin_foo(param)

如果需要,我可以在 类 的 API 模块 and/or 中更改 begin_foo 的实现。

函数的默认contextmanager装饰器的问题在于,除非在如您所知,with 上下文:

from contextlib import contextmanager

@contextmanager
def foo_bar():
    print('Starting')
    yield
    print('Finishing')
    return False

>>> with foo_bar():
...     print('in between')
...
Starting
in between
Finishing

>>> foo_bar()  # only returns the cm if merely invoked
<contextlib._GeneratorContextManager at 0x517a4f0>

可以创建另一个函数来执行进入和退出,对于任何 cm - 我很惊讶这还不存在:

def in_out(cm, *args, **kwargs):
    print('getting in')
    with cm(*args, **kwargs):
        print("we're in")

>>> in_out(foo_bar)
getting in
Starting
we're in
Finishing

可选地,将您 class 中的许多上下文管理器中的每一个重新实现为它们自己的上下文管理器 classes,并像 ContextDecorator docs' example 中那样使用它们,其中包括调用一个 cm 并作为一个直接调用:

from contextlib import ContextDecorator

class mycontext(ContextDecorator):
    def __enter__(self):
        print('Starting')
        return self

    def __exit__(self, *exc):
        print('Finishing')
        return False

>>> @mycontext()
... def function():
...     print('The bit in the middle')
...
>>> function()
Starting
The bit in the middle
Finishing

>>> with mycontext():
...     print('The bit in the middle')
...
Starting
The bit in the middle
Finishing