Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Hint: You can adjust the default video playback speed in your account settings.
Hint: You can set the default subtitles language in your account settings.
Sorry! Looks like there’s an issue with video playback 🙁 This might be due to a temporary outage or because of a configuration issue with your browser. Please see our video player troubleshooting guide to resolve the issue.

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.

01:18 While this implementation of Circle properly caches the computed diameter, it has the drawback that if you ever change the value of .radius, then .diameter won’t return a correct value.

01:34 In this example, you create a circle with a radius equal to 42.0. The .diameter property only computes the value the first time you access it.

01:46 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.

02:35 Here, the setter method of the .radius property resets the private ._diameter to 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 .radius.

03:36 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.

03:47 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.

04:27 Here, .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.

04:43 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 property(), cached_property() doesn’t block attribute mutations unless you provide a proper setter method.

05:08 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 property() and functools.cache(), as seen in the following example on-screen.

05:47 Here, you stack the @property decorator on top of the @cache decorator. The combination of both decorators builds a cached property that prevents mutations.

06:25 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 property.

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.