如何使用@contextmanager包装器作为标准函数

问题描述

这个问题类似于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

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...