cached-property - 类属性缓存装饰器
本文翻译整理自:https://github.com/pydanny/cached-property
文章目录
- 一、关于 cached-property
- 相关链接资源
- 关键功能特性
- 二、安装
- 三、使用指南
- 1、基础用法
- 2、手动清除缓存
- 3、线程安全版本
- 4、异步支持
- 5、缓存超时(TTL)
- 四、致谢
一、关于 cached-property
cached-property
是一个用于缓存类属性的装饰器工具。
相关链接资源
- github : https://github.com/pydanny/cached-property
- PyPI : https://pypi.python.org/pypi/cached-property
- Python 3.8+ 内置实现 : https://docs.python.org/3.12/library/functools.html#functools.cached_property
- 问题跟踪 : https://github.com/pydanny/cached-property/issues/16
关键功能特性
- 快速简便地缓存耗时或计算密集型的类属性
- 支持 Python 2 和 3(Python 3.8+ 已内置类似功能)
- 提供线程安全版本
threaded_cached_property
- 支持异步属性缓存
- 可设置缓存超时时间(TTL)
二、安装
pip install cached-property
三、使用指南
1、基础用法
定义包含昂贵计算属性的类:
class Monopoly:def __init__(self):self.boardwalk_price = 500@propertydef boardwalk(self):# 模拟耗时操作(如数据库查询或API调用)self.boardwalk_price += 50return self.boardwalk_price
测试效果(每次访问属性值都会变化):
>>> monopoly = Monopoly()
>>> monopoly.boardwalk
550
>>> monopoly.boardwalk
600
转换为缓存属性:
from cached_property import cached_propertyclass Monopoly(object):def __init__(self):self.boardwalk_price = 500@cached_propertydef boardwalk(self):self.boardwalk_price += 50return self.boardwalk_price
测试效果(值被缓存):
>>> monopoly = Monopoly()
>>> monopoly.boardwalk
550
>>> monopoly.boardwalk # 缓存生效
550
2、手动清除缓存
通过删除实例字典中的属性来清除缓存:
>>> del monopoly.__dict__['boardwalk']
>>> monopoly.boardwalk # 重新计算
600
3、线程安全版本
多线程环境下使用 threaded_cached_property
:
from cached_property import threaded_cached_propertyclass Monopoly:def __init__(self):self.boardwalk_price = 500@threaded_cached_propertydef boardwalk(self):sleep(1)self.boardwalk_price += 50return self.boardwalk_price
多线程测试:
>>> from threading import Thread
>>> threads = []
>>> for x in range(10):
>>> thread = Thread(target=lambda: monopoly.boardwalk)
>>> thread.start()
>>> threads.append(thread)
>>> [t.join() for t in threads]
>>> assert monopoly.boardwalk == 550 # 确保线程安全
4、异步支持
缓存异步属性:
from cached_property import cached_propertyclass Monopoly:def __init__(self):self.boardwalk_price = 500@cached_propertyasync def boardwalk(self):self.boardwalk_price += 50return self.boardwalk_price
异步调用示例:
>>> async def print_boardwalk():
... monopoly = Monopoly()
... print(await monopoly.boardwalk)
... print(await monopoly.boardwalk) # 使用缓存值
>>> asyncio.get_event_loop().run_until_complete(print_boardwalk())
550
550
注:异步版本不适用于多线程环境。
5、缓存超时(TTL)
设置缓存自动失效时间:
from cached_property import cached_property_with_ttlclass Monopoly(object):@cached_property_with_ttl(ttl=5) # 5秒后缓存失效def dice(self):return random.randint(2,12)
测试效果:
>>> monopoly = Monopoly()
>>> monopoly.dice
10
>>> monopoly.dice # 5秒内使用缓存
10
>>> sleep(6) # 等待缓存过期
>>> monopoly.dice # 重新计算
3
注:TTL 功能可能存在缓存清除不彻底的问题。
四、致谢
- 感谢 Pip、Django、Werkzeug 等项目的类似实现
- Reinout Van Rees 推荐原始装饰器方案
- @tinche 提供线程安全解决方案
- @bcho 贡献 TTL 功能
伊织 xAI 2025-04-27(日)