
本文介绍使用 python 3.8+ 的 `functools.cached_property` 装饰器,将耗时计算封装为惰性求值、仅执行一次且线程安全的属性,避免重复调用开销,同时保持代码简洁与类型提示完整性。
在面向对象编程中,常遇到一类典型场景:某个计算逻辑耗时较长(如加载大文件、查询数据库、复杂数值运算),但其结果在整个实例生命周期内恒定不变;而多个方法又频繁依赖该结果。若每次调用都重新执行,将造成显著性能浪费。传统做法(如在 __init__ 中预计算、或手动缓存至实例属性)存在明显缺陷:前者违背“延迟初始化”原则,可能浪费资源;后者需侵入式赋值、破坏方法语义,且难以兼顾类型安全与可读性。
functools.cached_property 正是为此而生——它是 Python 标准库提供的、专为惰性、一次性、实例级缓存设计的装饰器。它将普通方法转换为只读属性,在首次访问时执行计算并缓存结果,后续访问直接返回缓存值,且自动处理线程安全(内部使用 __dict__ 锁机制)。
以下为优化后的完整示例:
from functools import cached_property
from typing import List
class Example:
@cached_property
def my_value(self) -> int:
"""执行一次的耗时计算(例如:解析GB级配置、调用外部API、训练轻量模型)"""
print("✅ 执行耗时计算(仅首次触发)")
# 模拟真实耗时操作
import time
time.sleep(0.1)
return 42
def do_something_with_my_value(self) -> List[int]:
"""多次使用 my_value,但实际计算仅发生一次"""
return [x + self.my_value for x in range(1, 6)]
def another_method(self) -> str:
return f"Result: {self.my_value * 2}"使用效果验证:
obj = Example() print(obj.do_something_with_my_value()) # ✅ 输出:[43, 44, 45, 46, 47],并打印"✅ 执行耗时计算..." print(obj.another_method()) # ✅ 输出:"Result: 84",无额外计算日志 print(obj.my_value) # ✅ 直接访问属性,仍为 42,无重复执行
✅ 关键优势总结:
- 零侵入:无需修改 __init__ 或手动管理缓存属性;
- 类型友好:返回类型提示(-> int)完全保留,IDE 和类型检查器(如 mypy)可正常推导;
- 语义清晰:调用方式从 self.my_value() 变为 self.my_value,明确表达“这是一个稳定值”而非“一个可变行为”;
- 线程安全:标准库实现已内置同步机制,多线程环境下首次访问竞争亦能保证只执行一次;
- 内存可控:缓存绑定到实例 __dict__,实例销毁时自动释放,无内存泄漏风险。
⚠️ 注意事项:
- cached_property 仅适用于 实例属性,不支持类属性或静态方法;
- 缓存值不可重置(除非手动 del obj.my_value),若需动态刷新,请改用 @property + 自定义缓存逻辑,或考虑 lru_cache(maxsize=1)(但需确保参数恒定);
- Python
综上,@cached_property 是解决“单次执行、多次复用”问题最 Pythonic、最健壮的标准方案——它让性能优化与代码优雅不再矛盾。










