placeholderDecorator @Cached_property

Decorator @Cached_property

Study Python's cached_property decorator, how it stores computed values on instances, and where it fits in Django-style code.

Intro

Definition: Decorator that converts a method with a single self argument into a property cached on the instance.

from functools import cached_propertyimport json5 as jsonclass ConfigLoader:    @cached_property    def config(self):        with open('settings.json', 'r') as f:            return json.load(f)config = ConfigLoader().configprint(config)

Principle

The source logic is straightforward. When the decorated property is accessed for the first time, the original method is executed, and the computed result is stored in the instance’s __dict__. Later accesses return the cached value directly and no longer recompute it.

Source Code

# python3.13 functools.py################################################################################### cached_property() - property result cached as instance attribute################################################################################_NOT_FOUND = object()class cached_property:    def __init__(self, func):        self.func = func        self.attrname = None        self.__doc__ = func.__doc__        self.__module__ = func.__module__    def __set_name__(self, owner, name):        # Automatically called at the time the owning class owner is created.        if self.attrname is None:            self.attrname = name        elif name != self.attrname:            raise TypeError(                "Cannot assign the same cached_property to two different names "                f"({self.attrname!r} and {name!r})."            )    def __get__(self, instance, owner=None):        # Automatically called when the property is accessed.        if instance is None:  # True when accessed through the class, e.g. MyClass.attr            return self        if self.attrname is None:            raise TypeError(                "Cannot use cached_property instance without calling __set_name__ on it.")        try:            cache = instance.__dict__        except AttributeError:  # not all objects have __dict__ (e.g. class defines slots)            msg = (                f"No '__dict__' attribute on {type(instance).__name__!r} "                f"instance to cache {self.attrname!r} property."            )            raise TypeError(msg) from None        val = cache.get(self.attrname, _NOT_FOUND)        if val is _NOT_FOUND:            val = self.func(instance)            try:                cache[self.attrname] = val            except TypeError:                msg = (                    f"The '__dict__' attribute on {type(instance).__name__!r} instance "                    f"does not support item assignment for caching {self.attrname!r} property."                )                raise TypeError(msg) from None        return val    __class_getitem__ = classmethod(GenericAlias)

Scenarios

Frequently accessed, expensive-to-compute values, usually as read-only properties.

Danger

Note: if the data is written to, the cache will not automatically invalidate. You need to manually del or set the cached value.

Examples

CaseDescription
Cache ORM related fieldsStore database query results on the instance
Cache HTTP Request ObjectCache expensive properties such as parsed JSON and form data
# Not the same module, but the idea is the same# Demo Ref: https://medium.com/@esatyilmaz/introduction-c1306df1a84cfrom django.utils.functional import cached_propertyclass Book(models.Model):    title = models.CharField(max_length=50)    author = models.ForeignKey(Author, on_delete=models.CASCADE)    @cached_property    def author_full_name(self):        return self.author.full_name