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

Python 如何将@contextmanager(包装器)用作标准函数,python,contextmanager,Python,Contextmanager,这个问题类似于,但略有不同 我有很多类,每个类定义了几个@ContextManager: class MyClass: @contextmanager def begin_foo(param): [some code] yield [some code] [...] 我还有一个API模块,充当类的门面。 在API模块中,我发现我需要“包装”内部上下文管理器,以便在客户端代码中使用它们: @contextmanager d

这个问题类似于,但略有不同

我有很多类,每个类定义了几个@ContextManager:

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用作标准函数,而不需要使用。。。通行证构造?i、 e.就这样做:

# 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
装饰器的问题是,除非在
上下文中,否则无法同时调用
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>和
代码,如您所知:

从contextlib导入contextmanager
@上下文管理器
def foo_bar():
打印('开始')
产量
打印(“整理”)
返回错误
>>>使用foo_bar():
...     打印('中间')
...
启动
中间
完成
>>>foo_bar()#仅在仅调用时返回cm
您可以为任何cm创建另一个函数来执行enter和exit,我很惊讶这还不存在:

def输入输出(cm,*args,**kwargs):
打印(‘进入’)
带cm(*args,**kwargs):
打印(“我们在”)
>>>输入输出(食物棒)
进入
启动
我们进去了
完成
(可选)将类中的许多上下文管理器中的每一个重新实现为它们自己的上下文管理器类,并像中一样使用它们,其中包括作为cm和单个直接调用的调用:

从contextlib导入ContextDecorator
类mycontext(ContextDecorator):
定义输入(自我):
打印('开始')
回归自我
def uuu退出(自我,*exc):
打印(“整理”)
返回错误
>>>@mycontext()
... def函数():
...     打印('中间的位')
...
>>>函数()
启动
中间的钻头
完成
>>>使用mycontext():
...     打印('中间的位')
...
启动
中间的钻头
完成

是的,但这两种方法都有相同的问题——不同的函数名取决于函数是否用作CM。我需要相同的函数名…@Grodriguez:您试图编写一个函数,根据返回值的使用方式执行不同的操作。这总是会导致更多的问题。@Grodriguez:这听起来越来越像一个XY问题。(另外,
open
的行为与您要求的行为非常不同。)@Grodriguez啊,所以您提供的类具有上下文管理器,“客户端”是这些类的用户。我仍然建议,最简单的方法是为他们提供一个函数,该函数通过begin\u foo():pass执行
。没有办法创建一个替代调用选项或封装cm的func。“我很惊讶这个[in_out]还不存在”——可能是因为使用open_context()的
:pass更显式,并且没有隐藏涉及到上下文管理器。而且显式优于隐式;)我不确定
begin\u foo
首先应该是上下文管理器;可能是一个返回上下文管理器的函数?如果没有更多的细节,就很难判断。你的用例真的很奇怪——在很少的情况下,直接进入和退出上下文管理器是有意义的。也许上下文管理器承担了太多的责任,其中一些责任应该考虑在内。正如chepner所说,没有更多的信息就很难判断。可能比我现在想的要多,但如果“在‘foo’的上下文中不需要做任何事情”,那么为什么要将该函数同时用作上下文管理器和init函数呢。这些东西不能分开吗?@chepner也许。如果需要,我可以在MyClass或api模块中更改begin_foo。实际的需求是,客户机代码应该能够使用begin_foo作为CM或常规函数,使用相同的函数名。你能详细说明一下你的想法吗?(1/2)你说过“如果需要,我可以在API模块和/或类中更改begin_foo的实现。”在这种情况下,这里有另一个选项,但这是一个肮脏的diiirty解决方案,我绝对不建议考虑它:使用
异步
或多线程启动“计时器”当执行
begin\u foo()
时。然后用a(或其他什么),检查是否在0.001秒内调用了
\uuuu exit\uuuu()
部分或任何短的部分。