简体   繁体   中英

caching properties in python using cached_property construct

Recently I have seen use of cached_properties decorators in some of the codebases of python 3rd party packages like celery and django (it's also a utility function in Django) and the bottle web framework. There also exists a standalone package for using cached_properties . As far as I understand it's about avoiding heavy computations by providing caching behaviour.

Here is the source code stripped out that implements cached_property :

class cached_property(object):
    """
    A property that is only computed once per instance and then replaces itself
    with an ordinary attribute. Deleting the attribute resets the property."""

    def __init__(self, func):
        self.__doc__ = getattr(func, '__doc__')
        self.func = func

    def __get__(self, obj, cls):
        if obj is None:
            return self
        value = obj.__dict__[self.func.__name__] = self.func(obj)
        return value

and this is how it is used:

class Monopoly(object):

    def __init__(self):
        self.boardwalk_price = 500

    @cached_property
    def boardwalk(self):
        # Again, this is a silly example. Don't worry about it, this is
        #   just an example for clarity.
        self.boardwalk_price += 50
        return self.boardwalk_price

Now when we run it the price stays at $550.

>>> monopoly = Monopoly()
>>> monopoly.boardwalk
550
>>> monopoly.boardwalk
550
>>> monopoly.boardwalk
550

can't we simply store the result of first call to the attribute/property and use it later to avoid recomputation? I am sure this is a very silly question but what am I missing here?

ex:

monopoly = Monopoly()
>>> stored_value = monopoly.boardwalk
550

# now use stored_value instead of making a call to `monopoly.boardwalk`.

Sure you can, but what if there are many different places that use monopoly.boardwalk ? Now you have to make sure all those places cache the value locally. Or you could just make the Monopoly class responsible for the caching.

cached_property makes it easy to make the class responsible rather than the consumers of the attribute.

In case you are struggling to find examples of something being used in many different places: in a decently sized Django application you'd have templates, views, and back-end code all potentially touching a given model. Some of those places are in the Django framework itself (like the admin interface), and updating all those places when you add an expensive operation to your model is not practical. Using the decorator is practical; putting the responsibility with the model keeps all that other code simpler.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM