如何用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
相关产品推荐
相关产品推荐

