Cached Property
cached-property (current version 2.0.1) is a Python decorator for caching the results of properties in classes. It provides a straightforward way to memoize expensive computations, executing them only once per instance and storing the result as a regular attribute. The library offers basic, thread-safe, and time-based (TTL) caching mechanisms, as well as experimental async/await compatibility. It maintains an active development status with releases typically aligned with Python version support updates.
Warnings
- breaking Version 2.0.0 of `cached-property` officially dropped support for Python versions older than 3.8. Users on Python 2.7 or 3.7 and below should stick to `cached-property` version 1.5.2 or earlier.
- gotcha Python 3.8+ introduced `functools.cached_property` into the standard library. For basic property caching in Python 3.8+, `functools.cached_property` is generally preferred. `cached-property` provides additional features like time-to-live (TTL) and thread-safe variants that are not present in `functools.cached_property`.
- gotcha The `cached_property_with_ttl` and `threaded_cached_property_with_ttl` functions do not reliably allow manual invalidation of the cache. This means that once a value is cached with a TTL, it might not be possible to force a re-computation before the TTL expires.
- gotcha When combining `asyncio` with threading, especially with the `ttl` versions, be aware that most `asyncio` objects are not thread-safe. Running separate event loops in different threads can lead to unexpected behavior with cached values.
- gotcha Invalidating a `cached_property` is typically done by deleting the attribute (`del obj.property_name`). The library's quickstart sometimes shows `del obj.__dict__['property_name']` which is equivalent but can be less idiomatic. Both methods clear the cached value, allowing the property method to run again on next access.
Install
-
pip install cached-property
Imports
- cached_property
from cached_property import cached_property
- threaded_cached_property
from cached_property import threaded_cached_property
- cached_property_with_ttl
from cached_property import cached_property_with_ttl
- threaded_cached_property_with_ttl
from cached_property import threaded_cached_property_with_ttl
Quickstart
from cached_property import cached_property
class Monopoly:
def __init__(self):
self.boardwalk_price = 500
@cached_property
def boardwalk(self):
# In reality, this might represent a database call or time
# intensive task like calling a third-party API.
self.boardwalk_price += 50
return self.boardwalk_price
monopoly = Monopoly()
print(f"First access: {monopoly.boardwalk}")
print(f"Second access (cached): {monopoly.boardwalk}")
# To invalidate the cache, delete the attribute:
del monopoly.__dict__['boardwalk'] # Or del monopoly.boardwalk for simple cases
print(f"After invalidation: {monopoly.boardwalk}")