如何程序化给类所有方法加装饰器并跳过已有指定装饰器的方法
解决方案:给类方法批量加装饰器并跳过指定装饰的方法
嘿,这个需求我之前做缓存框架的时候刚好碰到过!Python确实没有直接暴露“查看方法上所有装饰器”的内置API,但我们可以换个思路——给not_cacheable装饰器打个自定义标记,这样就能在批量处理的时候轻松识别出哪些方法要跳过了。下面一步步来实现:
1. 给not_cacheable加个识别标记
我们可以给被not_cacheable装饰的方法附加一个自定义属性,比如_is_not_cacheable = True。这里记得用functools.wraps保留原方法的元数据,避免后续出现奇怪的问题:
import functools def not_cacheable(func): # 给原始方法打标记,方便后续识别 func._is_not_cacheable = True @functools.wraps(func) def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper
2. 修改cacheable装饰器,检查标记并跳过
接下来调整你的类装饰器cacheable,遍历类方法时先检查这个标记,如果存在就跳过该方法。另外要注意:绑定的实例方法需要通过__func__拿到原始函数,要是有多层装饰器,还得递归找到最原始的方法(通过__wrapped__属性):
import inspect def get_original_func(func): # 递归跳过所有装饰器wrapper,找到最原始的方法 while hasattr(func, '__wrapped__'): func = func.__wrapped__ return func def cacheable(decorator): def decorate(cls): for name, member in inspect.getmembers(cls): # 覆盖实例方法、类方法、静态方法三种情况 if inspect.ismethod(member): target_func = member.__func__ elif inspect.isfunction(member): target_func = member else: continue # 跳过非方法的属性 original_func = get_original_func(target_func) # 如果方法带有not_cacheable标记,就跳过 if getattr(original_func, '_is_not_cacheable', False): continue # 根据方法类型重新包装并替换原方法 if isinstance(member, classmethod): setattr(cls, name, classmethod(decorator(target_func))) elif isinstance(member, staticmethod): setattr(cls, name, staticmethod(decorator(target_func))) else: setattr(cls, name, decorator(member)) return cls return decorate
3. 完整使用示例
这里我写个带测试的完整代码,你可以直接运行验证效果:
import functools import inspect # 1. 定义带标记的not_cacheable装饰器 def not_cacheable(func): func._is_not_cacheable = True @functools.wraps(func) def wrapper(*args, **kwargs): print(f"👉 执行未缓存方法: {func.__name__}") return func(*args, **kwargs) return wrapper # 2. 定义示例缓存装饰器 def my_cache_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print(f"💾 缓存装饰器生效: {func.__name__}") return func(*args, **kwargs) return wrapper # 3. 定义批量处理的cacheable类装饰器 def get_original_func(func): while hasattr(func, '__wrapped__'): func = func.__wrapped__ return func def cacheable(decorator): def decorate(cls): for name, member in inspect.getmembers(cls): if inspect.ismethod(member): target_func = member.__func__ elif inspect.isfunction(member): target_func = member else: continue original_func = get_original_func(target_func) if getattr(original_func, '_is_not_cacheable', False): continue if isinstance(member, classmethod): setattr(cls, name, classmethod(decorator(target_func))) elif isinstance(member, staticmethod): setattr(cls, name, staticmethod(decorator(target_func))) else: setattr(cls, name, decorator(member)) return cls return decorate # 4. 测试类 @cacheable(my_cache_decorator) class C(object): @not_cacheable def method_1(self): pass def method_2(self, x): pass @classmethod @not_cacheable def class_method(cls): pass @staticmethod def static_method(): pass # 运行测试 c = C() c.method_1() # 只会打印"👉 执行未缓存方法: method_1",不会触发缓存 c.method_2(10) # 会打印"💾 缓存装饰器生效: method_2" C.class_method() # 只会打印"👉 执行未缓存方法: class_method" C.static_method() # 会打印"💾 缓存装饰器生效: static_method"
一些额外提示
- 装饰器顺序:如果你的方法有多个装饰器,确保
@not_cacheable是最内层的装饰器(直接贴在方法定义上),这样标记才会打在原始方法上;如果没法保证顺序,我们的get_original_func函数也能递归找到最原始的方法,不用担心。 - 方法类型:上面的代码已经处理了实例方法、类方法、静态方法三种常见情况,覆盖了大部分场景。
内容的提问来源于stack exchange,提问作者HIT_girl
相关产品推荐
相关产品推荐

