如何使用@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。。。通行证构造?也就是这样做:

# 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的实现。

函数的默认contextmanager装饰器的问题是,除非在with上下文中,否则无法同时调用__enter____exit__代码,正如您所知:

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

可选地,将类中的许多上下文管理器中的每一个重新实现为它们自己的上下文管理器类,并像在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

最新更新