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
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.
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.
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

This example demonstrates how to use the `cached_property` decorator to cache an expensive property computation. The `boardwalk` property will only execute its logic once per instance, returning the cached value on subsequent accesses until explicitly invalidated.

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}")