Python实例方法装饰器两大疑问:装饰时机与缓存属性重置失败
关于Python实例方法缓存装饰器的两个疑问解答
先看示例代码:
import functools def cache(func): @functools.wraps(func) # 保留原函数的__name__等属性 def wrapper(*args, **kwargs): if not wrapper.cache: print("caching...") wrapper.cache = func(*args, **kwargs) return wrapper.cache wrapper.cache = None return wrapper class Power: def __init__(self, exponent): self.exponent = exponent @cache def of(self, base): return base ** self.exponent # 测试过程 >>> cube = Power(3) >>> cube.of(2) caching... 8 >>> cube.of.cache 8 >>> cube.of.__dict__ {'__wrapped__': <function __main__.Power.of(self, base)>, 'cache': 8} >>> cube.of.cache = None ... AttributeError: 'method' object has no attribute 'cache'
问题1:装饰器接收未绑定方法为何仍能正常运行
类定义阶段,@cache装饰器确实会接收未绑定的of函数——Python中,类里的方法在类构建完成前就是普通函数,还没和任何实例绑定。但当你通过实例cube调用cube.of(2)时,Python会自动把cube作为第一个参数塞进*args,传给装饰后的wrapper函数。
wrapper调用原始的func(也就是未绑定的of函数)时,会把包含cube的args传进去,这就相当于手动给未绑定函数传入了self参数,让它变成了绑定调用,自然能正常执行base ** self.exponent的逻辑。
问题2:为何能读取cube.of.cache却无法赋值
cube.of是Python自动生成的绑定方法对象,它是一个临时包装器,用来关联实例和原始函数。当你读取cube.of.cache时,Python的属性查找机制会穿透这个方法对象,去它内部包装的wrapper函数(也就是类属性Power.of指向的函数)上查找cache属性,所以能成功读到值。
但当你尝试给cube.of.cache赋值时,Python会尝试给这个临时的方法对象添加属性,而方法对象本身不支持动态添加属性,因此触发AttributeError。
如果要重置缓存,应该直接操作类上的装饰后函数:Power.of.cache = None。另外要注意,当前装饰器实现的是类级别的共享缓存,所有Power实例的of方法会共用同一个缓存值;如果需要每个实例独立缓存,应该把缓存存在实例对象(self)上,而不是装饰器的wrapper函数里。
内容的提问来源于stack exchange,提问作者Andras Vanyolos
相关产品推荐
相关产品推荐

