Python装饰类方法时,如何访问被装饰方法的self?
如何访问被装饰类方法的self?
当使用类实现的装饰器修饰类的实例方法时,会出现装饰器内的self指向装饰器实例而非被装饰类(如示例中的A)实例的问题,导致无法访问类实例的属性,同时参数传递也存在错误。
示例错误代码:
class Decorator: def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): print(self.func.__name__) self.counter += 1 return self.func(*args, **kwargs) class A: def __init__(self): self.counter = 0 @Decorator def method1(self): pass
运行后报错:
5 def __call__(self, *args, **kwargs): ----> 6 self.counter += 1 7 return self.func(*args, **kwargs) AttributeError: 'Decorator' object has no attribute 'counter'
问题原因
用类装饰器修饰实例方法后,原方法会被替换成装饰器的实例。调用A().method1()时,实际执行的是装饰器实例的__call__方法,此时__call__里的self是Decorator的实例,而非A的实例;而A的实例会作为第一个参数被传入__call__的args列表中。
解决方案
方案1:直接从参数中提取类实例
修改装饰器的__call__方法,从args中获取A的实例(即原方法的self),直接操作该实例的属性:
class Decorator: def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): print(self.func.__name__) # args[0]就是类A的实例(原方法的self) a_instance = args[0] a_instance.counter += 1 return self.func(*args, **kwargs) class A: def __init__(self): self.counter = 0 @Decorator def method1(self): pass # 测试 a = A() a.method1() print(a.counter) # 输出1
方案2:让装饰器实现描述符协议(更规范)
实例方法本质是描述符,让装饰器类实现__get__方法,确保装饰器能正确绑定类实例,这样__call__可以直接拿到A的实例:
from functools import partial class Decorator: def __init__(self, func): self.func = func def __get__(self, instance, owner): # 将类实例绑定到装饰器的调用逻辑中 return partial(self.__call__, instance) def __call__(self, instance, *args, **kwargs): print(self.func.__name__) instance.counter += 1 return self.func(instance, *args, **kwargs) class A: def __init__(self): self.counter = 0 @Decorator def method1(self): pass # 测试 a = A() a.method1() print(a.counter) # 输出1
说明
两种方案都能实现需求:打印被装饰方法的名称,同时更新A实例的counter属性。方案2更符合Python的描述符机制,尤其在装饰复杂方法时更可靠。
内容的提问来源于stack exchange,提问作者Muhammad Yasirroni
相关产品推荐
相关产品推荐

