带参数的类作为装饰器时,如何获取类实例而非__call__返回的函数?
带参数的类作为装饰器时,如何获取类实例而非__call__返回的函数?
嘿,我太懂你遇到的这个坑了——之前写的类装饰器最后把被装饰的函数变成了包装后的普通函数,导致想调用metadata()这类自定义方法根本找不到入口,对吧?核心问题就是没摸准带参数的类装饰器的执行逻辑,咱们一步步把它掰正。
首先得把带参数类装饰器的执行流程理清楚:
- 当你写
@step(tags=[...], ...)时,Python会先调用step的__init__,把装饰器参数传进去,先创建一个step类的实例; - 紧接着,这个实例会被自动调用,参数就是你要装饰的那个函数(比如
test); - 最后,这次调用的返回值会被赋值给
test变量。
你之前的写法里,__call__返回的是包装后的函数,所以test就变成了普通函数,自然没法调用类的实例方法。咱们要做的,就是让__call__返回类实例本身,这样test就是step的实例,既能当函数调用,又能调用metadata()这类自定义方法。
下面是修正后的完整代码,完全贴合你的需求:
class step: def __init__(self, version, description, tags): # 第一步:先接收装饰器的参数,此时还没拿到要装饰的函数 self._version = version self._description = description self._tags = tags self._wrapped_func = None # 存被orchestrator装饰后的函数 self._original_func = None # 可选:保留原函数,方便后续扩展 def __call__(self, func): # 第二步:接收被装饰的函数,完成装饰逻辑 self._original_func = func # 把orchestrator装饰器应用到原函数上,保留它的原有功能 self._wrapped_func = orchestrator( tags=self._tags, version=self._version )(func) # 关键操作!返回实例本身,这样被装饰后的变量就是step的实例 return self def __call__(self, *args, **kwargs): # 第三步:当用户像调用普通函数一样用test(1,2)时,执行这段逻辑 if not self._wrapped_func: raise ValueError("还没有装饰任何函数哦!") # 直接调用被orchestrator装饰后的函数,完全保留原装饰器的能力 return self._wrapped_func(*args, **kwargs) def metadata(self): # 你想要的元数据方法,直接返回整理好的字典 return { "version": self._version, "description": self._description, "tags": self._tags, } # 后续想加新方法直接在这里加就行,比如给标签新增内容的方法 def add_tags(self, new_tags): if isinstance(new_tags, list): self._tags.extend(new_tags) else: self._tags.append(new_tags) # 如果需要同步更新orchestrator里的元数据,这里可以补充对应逻辑
用这个装饰器的方式和你最初想的完全一样:
@step( tags = ["test"], description = "这是一个示例加法函数", version = "0.1" ) def test(a,b): return a+b
现在你再试试这些操作:
- 调用
test(2,3):会执行被orchestrator装饰后的函数,返回5,完全保留原装饰器的所有功能; - 调用
test.metadata():会直接返回你定义的元数据字典{"version": "0.1", "description": "这是一个示例加法函数", "tags": ["test"]}; - 调用
test.add_tags(["new_tag1", "new_tag2"]):可以给实例添加新标签,再调用metadata()就能看到更新后的内容。
这个写法的优势太明显了:
- 完全符合Python带参数装饰器的执行逻辑,流程清晰不绕弯;
- 被装饰后的变量是类实例,兼顾了“像普通函数一样调用”和“能调用自定义方法”两个核心需求;
- 扩展性拉满,后续要加新功能直接在
step类里加方法就行,不用大改原有结构。
之前踩坑主要是没搞懂带参数类装饰器的两步流程,还错误地让__call__返回了包装函数而非实例本身,现在这个写法就完美规避了这些问题,绝对是Pythonic的标准实现方式!
备注:内容来源于stack exchange,提问作者BBG
相关产品推荐
相关产品推荐

