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

如何用Step类装饰实例方法并替换为Step对象?

解决Step类装饰实例方法的元数据存储与__get__实现问题

核心问题拆解

你需要的是一个兼具装饰器能力和直接实例化能力的Step类:既要把实例方法替换为携带StepOptions元数据的Step对象,又要避免__get__返回partial导致的元数据丢失或行为异常——本质是不能破坏Step对象的完整性,同时要正确处理实例方法的绑定上下文。

优化实现方案

让Step类同时实现描述符协议和可调用协议,既保留Step对象本身的元数据,又能正确绑定实例上下文执行原方法:

代码实现

from typing import Callable, Optional, Any

class StepOptions:
    def __init__(self, name: str, timeout: int = 30):
        self.name = name
        self.timeout = timeout

class Step:
    def __init__(self, func: Callable, options: Optional[StepOptions] = None):
        self.func = func
        self.options = options or StepOptions(name=func.__name__)
        # 保留原方法的元信息,避免装饰器破坏方法签名
        self.__name__ = func.__name__
        self.__doc__ = func.__doc__

    def __get__(self, instance: Any, owner: type) -> "Step":
        # 关键:返回绑定了实例的Step对象,而非partial
        bound_step = Step(self.func, self.options)
        bound_step._bound_instance = instance
        return bound_step

    def __call__(self, *args: Any, **kwargs: Any) -> Any:
        # 区分绑定实例和直接调用场景,正确执行原函数
        if hasattr(self, "_bound_instance"):
            return self.func(self._bound_instance, *args, **kwargs)
        return self.func(*args, **kwargs)

# 测试场景1:装饰实例方法
class TestCase:
    @Step(options=StepOptions(name="登录步骤", timeout=60))
    def login(self, username: str, password: str):
        print(f"执行登录:{username}")
        return True

# 测试场景2:直接实例化Step
def logout():
    print("执行登出")
    return True

logout_step = Step(logout, StepOptions(name="登出步骤"))

# 验证效果
test_case = TestCase()
print(test_case.login.options.name)  # 输出:登录步骤
test_case.login("admin", "123456")   # 输出:执行登录:admin

print(logout_step.options.name)      # 输出:登出步骤
logout_step()                        # 输出:执行登出

关键细节说明

  • __get__方法:返回绑定了实例的Step对象,而非partial,确保始终能访问到Step的options元数据,不会丢失
  • __call__方法:区分绑定实例和直接调用两种情况,正确传递上下文执行原函数
  • 保留原方法元信息:通过复制__name__和__doc__,避免装饰器破坏方法的签名和文档

额外优化建议

  • 兼容类方法/静态方法:在__get__中判断instance是否为None,结合owner处理类方法的绑定逻辑
  • 元数据便捷访问:给Step类添加@property方法,比如@property def step_name(self): return self.options.name,简化元数据读取
  • 执行上下文传递:在__call__中把Step对象本身作为参数传入原方法,方便测试执行器获取元数据,比如修改为self.func(self._bound_instance, self, *args, **kwargs),原方法可通过step参数直接读取options

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 15:08:14