macro_controller
macro_controller

Reputation: 1519

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?

Upvotes: 0

Views: 2105

Answers (1)

Alex Hall
Alex Hall

Reputation: 36033

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

Upvotes: 3

Related Questions