首页 > 解决方案 > 如何使用@contextmanager(包装器)作为标准函数

问题描述

这个问题类似于Python:标准函数和上下文管理器?,但略有不同。

我有许多类,每个类都定义了几个@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 模块和/或类中更改 begin_foo 的实现。

标签: pythoncontextmanager

解决方案


函数的默认contextmanager装饰器的问题在于,除非在上下文中,否则无法同时调用和代码,正如您所知道的:__enter____exit__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>

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

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

或者,将您的类中的许多上下文管理器中的每一个重新实现为它们自己的上下文管理器类,并像在ContextDecorator文档示例中一样使用它们,其中包括作为 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

推荐阅读