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

如何为wrapt装饰的BoundFunctionWrapper添加tag属性以标记方法

为wrapt装饰的方法添加tag属性并动态查找

我希望给wrapt装饰的方法对应的BoundFunctionWrapper添加名为tag的属性,这样就能检查实例中带有特定tag值的方法。在多个类上使用该装饰器后,还能查找并执行所有带有指定tag的方法。但当前代码实现无法直接通过实例的方法访问tag属性,请问该方案是否可行?如果可行,我的装饰器实现缺少了什么?有没有更好的动态实现方式?我倾向于动态实现,因为用字典等静态标记方式可能会导致人为错误。

原始代码

import wrapt
import inspect

def tag_method(tag_message:str):
    @wrapt.decorator
    def wrapper(wrapped, instance, args, kwargs):
        #  do some other wrapper stuff besides just tagging
        return wrapped(*args, **kwargs)
    setattr(wrapper, 'tag', tag_message)
    return wrapper


# Test Code
if __name__ == '__main__':
    class MyClass:
        def __init__(self) -> None:
            pass

        @tag_method('value_method')
        def return_value(self, value:int) -> int:
            return value

    instance = MyClass()
    print(instance.return_value(5))
    print(inspect.getmembers(instance, predicate=inspect.ismethod))
    print(inspect.getmembers(instance, predicate=inspect.ismethod)[1])
    print(inspect.getmembers(instance, predicate=inspect.ismethod)[1][1])  # why does this suddenly evaluate to the bound method instead of the BoundFunctionWrapper?
    print(inspect.getmembers(instance, predicate=inspect.ismethod)[1][1].tag)  # want to print the assigned tag 'value_method'

可行性与问题分析

这个方案完全可行。你当前实现的核心问题是:把tag属性添加到了wrapt的Decorator对象上,而非最终生成的FunctionWrapper(或绑定实例后的BoundFunctionWrapper)上。当你访问实例的方法时,得到的是绑定后的BoundFunctionWrapper代理对象,它并不会继承外层Decorator对象的tag属性,因此无法直接通过method.tag获取值。

修正后的装饰器实现

修改装饰器,确保tag属性被添加到最终的包装函数(FunctionWrapper)上,这样绑定实例后的BoundFunctionWrapper会代理该属性:

import wrapt
import inspect

def tag_method(tag_message: str):
    @wrapt.decorator
    def wrapper(wrapped, instance, args, kwargs):
        # 这里可以添加你的其他包装逻辑
        return wrapped(*args, **kwargs)
    
    # 自定义装饰器逻辑,给生成的FunctionWrapper添加tag
    def decorator(wrapped):
        wrapped_func = wrapper(wrapped)
        setattr(wrapped_func, 'tag', tag_message)
        return wrapped_func
    
    return decorator


# 测试代码
if __name__ == '__main__':
    class MyClass:
        def __init__(self) -> None:
            pass

        @tag_method('value_method')
        def return_value(self, value:int) -> int:
            return value
        
        @tag_method('print_method')
        def print_hello(self):
            print("Hello World")

    instance = MyClass()
    print(instance.return_value(5))
    
    # 获取所有带指定tag的方法
    def find_tagged_methods(instance, target_tag):
        tagged_methods = []
        for name, method in inspect.getmembers(instance, predicate=inspect.ismethod):
            if hasattr(method, 'tag') and method.tag == target_tag:
                tagged_methods.append((name, method))
        return tagged_methods
    
    # 查找并执行tag为'value_method'的方法
    value_methods = find_tagged_methods(instance, 'value_method')
    for name, method in value_methods:
        print(f"执行方法 {name},结果:{method(10)}")
    
    # 查找并执行tag为'print_method'的方法
    print_methods = find_tagged_methods(instance, 'print_method')
    for name, method in print_methods:
        print(f"执行方法 {name}:")
        method()

关键说明

  1. 属性传递逻辑:wrapt的BoundFunctionWrapper是FunctionWrapper的绑定版本,它会代理原FunctionWrapper的属性,因此给FunctionWrapper添加tag后,绑定实例的方法也能直接访问method.tag。
  2. 动态查找优势:这种方式完全依赖方法对象自身的属性,无需额外维护静态字典,避免了手动维护注册表可能带来的错误,符合你想要的动态实现需求。
  3. 兼容性:inspect.ismethod会将BoundFunctionWrapper识别为普通方法,但这不影响属性访问,因为它本质是一个代理对象,会透传对tag属性的访问请求。

扩展优化

如果需要跨多个实例或类批量查找带tag的方法,可以扩展工具函数,支持传入类或实例列表:

def find_tagged_methods_in_objects(objects, target_tag):
    all_tagged = []
    for obj in objects:
        methods = find_tagged_methods(obj, target_tag)
        all_tagged.extend([(obj, name, method) for name, method in methods])
    return all_tagged

内容的提问来源于stack exchange,提问作者riley-lunar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 08:45:01