问题描述
这个问题类似于Python: standard function and context manager?,但略有不同。
我有很多类,每个类都定义了几个@contextmanagers:
fviz_cluster(clara.res,palette = c("#fcff00","#ffb14e","#51ff38","#a30000","#ffc8ff","#9d02d7","#1000e5","#12890b","#00c1ff"),# color palette
ellipse.type = "t",# Concentration ellipse
geom = "point",pointsize = 1,ggtheme = theme_classic())
我还有一个 API 模块,用作类的外观。 在 API 模块中,我发现我需要“包装”内部上下文管理器,以便我可以在客户端代码中使用它们:
class MyClass:
@contextmanager
def begin_foo(param):
[some code]
yield
[some code]
[...]
我的客户端代码现在是这样的:
@contextmanager
def begin_foo(param):
with myobj.begin_foo(param):
yield
问题:有没有一种方法可以在第二种情况下使用 begin_foo 作为标准函数,而无需 with ... pass 构造?即只是这样做:
# 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
如果需要,我可以在 API 模块和/或类中更改 begin_foo 的实现。
解决方法
函数的默认 contextmanager
装饰器的问题是无法同时调用 __enter__
和 __exit__
代码,除非在 {{1} } 上下文,如您所知:
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>
可选地,将您类中的众多上下文管理器中的每一个重新实现为它们自己的上下文管理器类,并像在 ContextDecorator
docs' example 中一样使用它们,其中包括作为 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