Cached Property
raw JSON → 2.0.1 verified Tue May 12 auth: no python install: verified
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.
pip install cached-property Common errors
error ImportError: cannot import name 'cached_property' from 'functools' ↓
cause This error occurs when attempting to import `cached_property` from Python's built-in `functools` module in a Python version prior to 3.8, where it was not yet available, or when confusing the standard library `cached_property` with the external `cached-property` package.
fix
To use the
cached-property library (version 2.0.1 requires Python 3.8+), ensure it's installed (pip install cached-property) and import it directly: from cached_property import cached_property. If you specifically intend to use the standard library functools.cached_property, ensure your Python environment is version 3.8 or newer. error AttributeError: 'cached_property' object has no attribute 'setter' ↓
cause The `cached_property` decorator (both from the external `cached-property` library and `functools`) is designed for properties whose values are computed once and then cached as effectively immutable attributes; it does not inherently support the `.setter` method like a standard `property` decorator.
fix
If you need a writable property with caching, you typically manage cache invalidation manually within a custom setter (e.g.,
del self.__dict__['my_property']) or by defining a standard @property with a @my_property.setter and implementing explicit caching logic there. error TypeError: Cannot use cached_property instance without calling __set_name__ on it. ↓
cause This error commonly arises when `cached_property` is used with classes that define `__slots__` but do not include `'__dict__'` in their `__slots__` definition. `cached_property` relies on writing the cached value to the instance's `__dict__`, which is unavailable or immutable in such cases.
fix
To use
cached_property with __slots__, you must include '__dict__' in your __slots__ definition (e.g., __slots__ = ('my_attribute', '__dict__')) to provide a mutable dictionary for caching. Alternatively, avoid __slots__ if memory optimization is not critical, or consider using functools.lru_cache on a regular method if applicable. error AttributeError: 'cached_property' object has no attribute 'lock' ↓
cause This error specifically occurs in Python 3.12 and later versions when code attempts to access an undocumented `lock` attribute on a `cached_property` instance. The internal, class-wide locking mechanism of `functools.cached_property` (which caused performance issues) was removed in Python 3.12.
fix
For thread-safe caching with the
cached-property library, use the threaded_cached_property decorator (from cached_property import threaded_cached_property). If you were relying on the removed functools.cached_property lock, you must implement explicit locking within your getter function or around access points to ensure thread synchronization. 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. ↓
fix Upgrade your Python environment to 3.8+ or pin `cached-property` to a compatible version (e.g., `cached-property<2.0.0` for Python <3.8).
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`. ↓
fix For basic caching in Python 3.8+, consider using `from functools import cached_property`. If you require TTL or explicit thread-safe caching, `cached-property` remains a viable choice.
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. ↓
fix Be aware of this limitation when using TTL features. If immediate invalidation is critical, consider using the base `cached_property` and managing invalidation manually (`del obj.property_name`) or designing around the TTL expiration.
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. ↓
fix Avoid mixing `asyncio` with threading if possible. If necessary, ensure proper synchronization and careful management of event loops across threads to prevent data corruption or incorrect 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. ↓
fix Prefer `del obj.property_name` for simple cache invalidation. Ensure your application logic correctly triggers invalidation when the underlying data changes, as the decorator itself does not automatically detect state changes.
Install compatibility verified last tested: 2026-05-12
python os / libc status wheel install import disk
3.10 alpine (musl) wheel - 0.09s 17.8M
3.10 alpine (musl) - - 0.08s 17.8M
3.10 slim (glibc) wheel 1.5s 0.06s 18M
3.10 slim (glibc) - - 0.06s 18M
3.11 alpine (musl) wheel - 0.17s 19.6M
3.11 alpine (musl) - - 0.17s 19.6M
3.11 slim (glibc) wheel 1.6s 0.13s 20M
3.11 slim (glibc) - - 0.13s 20M
3.12 alpine (musl) wheel - 0.34s 11.5M
3.12 alpine (musl) - - 0.38s 11.5M
3.12 slim (glibc) wheel 1.5s 0.32s 12M
3.12 slim (glibc) - - 0.34s 12M
3.13 alpine (musl) wheel - 0.36s 11.3M
3.13 alpine (musl) - - 0.41s 11.1M
3.13 slim (glibc) wheel 1.5s 0.31s 12M
3.13 slim (glibc) - - 0.33s 12M
3.9 alpine (musl) wheel - 0.08s 17.3M
3.9 alpine (musl) - - 0.08s 17.3M
3.9 slim (glibc) wheel 1.7s 0.06s 18M
3.9 slim (glibc) - - 0.07s 18M
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 last tested: 2026-04-24
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}")