Caching Computed Attributes
00:00 Caching Computed Attributes. Sometimes you have a given computed attribute that you use frequently. Constantly repeating that same computation may be unnecessary and expensive. To work around this problem, you can cache the computed value and save it in a non-public dedicated attribute for further reuse.
00:23 To prevent unexpected behavior, you need to think of the mutability of the input data. If you have a property that computes its value from a constant input value, then the result will never change. In that case, you can compute the value just once.
While this implementation of
Circle properly caches the computed diameter, it has the drawback that if you ever change the value of
.diameter won’t return a correct value.
In this example, you create a circle with a radius equal to
.diameter property only computes the value the first time you access it.
That’s why you see a delay in the first execution and no delay in the second. Note that even though you change the value of
.radius, the diameter has stayed the same.
02:05 If the input value for a computed attribute mutates, then you need to recalculate the attribute.
Here, the setter method of the
.radius property resets the private
None every time you change the value of
.radius. With this update,
.diameter recalculates its value the first time you access it after every mutation of
As you can see,
Circle works correctly now. It computes the diameter the first time you access it and also every time you change the radius.
Another option to create cached properties is to use
functools.cached_property() from the standard library. This function works as a decorator that allows you to transform a method into a cached property.
04:00 The property computes its value only once and caches it as a normal attribute during the lifetime of the instance.
.diameter computes and caches its value the first time you access it. This kind of implementation is suitable for those computations in which the input values don’t mutate.
Again, here, you can see it in action. When you access
.diameter, you get its computed value. That value remains the same from this point on. However, unlike
cached_property() doesn’t block attribute mutations unless you provide a proper setter method.
This can be seen here, as it’s possible to update the diameter to
200. If you want to create a cached property that doesn’t allow modification, then you can use
functools.cache(), as seen in the following example on-screen.
Here, you stack the
@property decorator on top of the
@cache decorator. The combination of both decorators builds a cached property that prevents mutations.
Here, when you try to assign a new value to
.diameter, you get an
AttributeError because the setter functionality comes from the internal descriptor of
06:37 In the next section of the course, you’ll see how to log when an attribute is accessed or mutated.
Become a Member to join the conversation.