You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将@contextmanager包装器改造为可直接调用的标准函数

实现方案

有三种常用的实现方式,你可以根据自己的业务场景选择:

方案1:拆分独立接口(最推荐)

直接在API层提供两个语义明确的接口,完全避免误用,维护成本最低:

from contextlib import contextmanager

# 供需要上下文的场景使用
@contextmanager
def begin_foo_context(param):
    with myobj.begin_foo(param):
        yield

# 供直接调用的场景使用
def begin_foo(param):
    with begin_foo_context(param):
        pass

客户端调用示例:

# 场景1:需要在上下文中执行业务逻辑
with begin_foo_context(param):
    # 你的业务代码
    ...

# 场景2:仅执行入口、出口逻辑
begin_foo(param)

方案2:单接口加参数控制

如果不想拆分接口,可以加可选参数切换行为:

from contextlib import contextmanager

@contextmanager
def _begin_foo_cm(param):
    with myobj.begin_foo(param):
        yield

def begin_foo(param, use_context:bool = True):
    if use_context:
        return _begin_foo_cm(param)
    with _begin_foo_cm(param):
        pass

客户端调用示例:

# 场景1:默认行为兼容原有写法
with begin_foo(param):
    ...

# 场景2:指定参数关闭上下文模式
begin_foo(param, use_context=False)

方案3:无参数自动兼容两种用法

如果不想修改原有调用代码,也不想加参数,可以返回同时支持上下文协议和可调用的包装对象:

from contextlib import contextmanager

class CMAdapter:
    def __init__(self, cm):
        self._cm = cm
    def __enter__(self):
        return self._cm.__enter__()
    def __exit__(self, *exc_info):
        return self._cm.__exit__(*exc_info)
    def __call__(self):
        with self._cm:
            pass

@contextmanager
def _begin_foo_cm(param):
    with myobj.begin_foo(param):
        yield

def begin_foo(param):
    return CMAdapter(_begin_foo_cm(param))

客户端调用示例:

# 场景1:原有with写法完全不用修改
with begin_foo(param):
    ...

# 场景2:调用返回值即可
begin_foo(param)()

内容的提问来源于stack exchange,提问作者Grodriguez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.17 19:45:04