为什么使用类作为方法装饰器时args参数中不存在self?
问题成因
本质是Python的描述符协议实现差异导致的:
- 普通Python函数原生实现了
__get__方法,符合描述符规范。当你用函数类型的装饰器包装类方法时,返回的wrapper仍然是函数对象,访问实例的对应方法时,会自动触发描述符绑定逻辑,将实例对象作为第一个参数self注入到参数列表中,所以你能在args[0]拿到self。 - 你自定义的
DecoratorClass类没有实现__get__方法,不满足方法绑定的要求。当你访问实例的count_b属性时,直接返回的是DecoratorClass的实例对象,调用它的__call__方法时不会自动注入实例作为第一个参数,因此args是空元组,访问args[0]就触发了索引越界错误。
解决方法
给DecoratorClass实现__get__方法,兼容描述符协议的绑定逻辑即可,修正后的代码如下:
import functools from types import MethodType class DecoratorClass: def __init__(self, func): functools.update_wrapper(self, func) self.func = func # 新增__get__方法实现描述符协议 def __get__(self, instance, owner): if instance is None: return self # 将当前装饰器实例绑定到调用方实例,返回绑定后的方法 return MethodType(self, instance) def __call__(self, *args, **kwargs): # 此时args[0]就是被绑定的self对象 print("args:", args) print("kwargs:", kwargs) print("Counting with:", args[0].name) self.func(*args, **kwargs) def decorator_func(func): @functools.wraps(func) def wrapper(*args, **kwargs): print("args:", args) print("kwargs:", kwargs) print("Counting with:", args[0].name) func(*args, **kwargs) return wrapper class Counter: def __init__(self, name, count): self.name = name self.count = count @decorator_func def count_a(self): for i in range(self.count): print(i) @DecoratorClass def count_b(self): for i in range(self.count): print(i) c = Counter('my counter', 3) c.count_a() print() c.count_b()
运行后两个方法都可以正常输出,不会再触发索引错误。
内容的提问来源于stack exchange,提问作者Dominick Pastore
相关产品推荐
相关产品推荐

