Decorator @Cached_property
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.
Note: if the data is written to, the cache will not automatically invalidate. You need to manually del or set the cached value.
Examples
| Case | Description |
|---|---|
| Cache ORM related fields | Store database query results on the instance |
| Cache HTTP Request Object | Cache 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
