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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 23:54:50