Search code examples
pythoncachingdecoratorpython-decorators

Cache results of properties in python through a decorator


In my scenario, I have a class with many properties. The properties don't take any parameters, they are heavy in calculation time, and their results should not change during the program lifecycle.

I want to cache the results of those properties, so the heavy calculation is done only once. The approach that I took is with decorators:

def cached(f):
    def wrapper(*args):
        # get self
        class_self = args[0]
        cache_attr_name = '_' + f.__name__

        if hasattr(class_self, cache_attr_name):
            return getattr(class_self, cache_attr_name)

        else:
            result = f(*args)
            setattr(class_self, cache_attr_name, result)
            return result

    return wrapper

and then in the cached class members:

class MyClass():
    @property
    @cached
    def heavy_prop(self):
        # In the actual class, the heavy calculation happens here
        return 1

Any ideas for a better/other solution for this case?


Solution

  • For Python 3.8, use the built in cached_property: https://docs.python.org/dev/library/functools.html#functools.cached_property

    For older versions, use the library https://github.com/pydanny/cached-property

    Or just use this code:

    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.
    
        Based on https://github.com/pydanny/cached-property/blob/master/cached_property.py
        """
    
        def __init__(self, func):
            self.__doc__ = func.__doc__
            self.func = func
    
        def cached_property_wrapper(self, obj, _cls):
            if obj is None:
                return self
    
            value = obj.__dict__[self.func.__name__] = self.func(obj)
            return value
    
        __get__ = cached_property_wrapper