Functools -- cached_property
cached_property 是 Python 3.8+ 引入的装饰器:
from functools import cached_property
它用于 把类的方法变成只计算一次的属性,并把结果缓存下来。
换句话说:
- 📌 第一次访问时运行函数、计算结果
- 📌 后续访问不再执行函数,而是直接从缓存里取值
- 📌 用于那些计算昂贵但又不想在初始化时执行的属性
from functools import cached_property
import time
class Data:
@cached_property
def expensive_value(self):
print("计算中...")
time.sleep(2)
return 42
d = Data()
print(d.expensive_value) # 会打印 "计算中...",然后输出 42
print(d.expensive_value) # 直接返回 42,不再计算
👉 第二次访问时 不会再执行函数。
普通 @property:
- 每访问一次就计算一次 -> 慢
- 不适合昂贵操作
@cached_property:
- 只执行一次
- 后面全部用缓存
- 缓存的值存储在实例的
__dict__里
访问 obj.attr 时:
- 第一次调用方法
- 执行方法体,得到结果
- 把结果放到
obj.__dict__['attr'] - 下次访问直接从 dict 中取值
所以你可以手动清除缓存:
del obj.expensive_value
下次访问会重新计算。
class DB:
@cached_property
def engine(self):
return create_engine(...)
访问 db.engine 才真正建立连接。
class Config:
@cached_property
def settings(self):
with open("config.json") as f:
return json.load(f)
不会每次都读文件。
class User:
@cached_property
def profile(self):
return load_profile_from_db(self.id)
class Stock:
@cached_property
def summary(self):
return compute_summary(self.data)
| 场景 | 用 cached_property | 用 cache |
|---|---|---|
| 绑定到对象实例 | ✅ | ❌ |
| 想缓存方法结果(与 self 绑定) | ✅ | ❌ |
| 想缓存普通函数 | ❌ | ✅ |
| 想缓存多种参数的结果 | ❌ | ✅(cache 支持不同参数缓存) |
| 想懒加载属性 | ✅ | ❌ |
cached_property 是 instance-level cache cache 是 function-level cache
del obj.expensive_value
再次访问会重新计算。
如果你想一次性清空全部 cached_property:
for key in list(obj.__dict__.keys()):
del obj.__dict__[key]
@cached_property
async def value(): # 错误
不能这么写。
若需要 async 版,我能帮你写 async_cached_property。
例如:
@cached_property
def data(self):
return {"a": 1}
你修改它:
obj.data["a"] = 999
缓存不会刷新,小心副作用。
如果希望防止用户覆盖 cached_property:
class Safe:
@cached_property
def value(self):
return 123
@value.setter
def value(self, v):
raise AttributeError("value is read-only")
from functools import cached_property
from loguru import logger
def logged_cached_property(func):
@cached_property
def wrapper(self):
logger.debug(f"Computing {func.__name__}")
return func(self)
return wrapper
使用:
class Data:
@logged_cached_property
def info(self):
return load_big_data()
- 用法:@cached_property
- 目的:把计算一次的值缓存为属性
- 首次访问:执行函数并缓存
- 再次访问:直接读取缓存,不执行函数
- 缓存位置:实例
__dict__ - 清除缓存:
del obj.attr - 适用场景:懒加载、昂贵计算、配置读取
- 不适用:async、会变化的值、需要参数的函数