Caching class attributes in Python Caching class attributes in Python python python

Caching class attributes in Python


Python ≥ 3.8@property and @functools.lru_cache have been combined into @cached_property.

import functoolsclass MyClass:    @functools.cached_property    def foo(self):        print("long calculation here")        return 21 * 2

Python ≥ 3.2 < 3.8

You should use both @property and @functools.lru_cache decorators:

import functoolsclass MyClass:    @property    @functools.lru_cache()    def foo(self):        print("long calculation here")        return 21 * 2

This answer has more detailed examples and also mentions a backport for previous Python versions.

Python < 3.2

The Python wiki has a cached property decorator (MIT licensed) that can be used like this:

import random# the class containing the property must be a new-style classclass MyClass(object):   # create property whose value is cached for ten minutes   @cached_property(ttl=600)   def randint(self):       # will only be evaluated every 10 min. at maximum.       return random.randint(0, 100)

Or any implementation mentioned in the others answers that fits your needs.
Or the above mentioned backport.


I used to do this how gnibbler suggested, but I eventually got tired of the little housekeeping steps.

So I built my own descriptor:

class cached_property(object):    """    Descriptor (non-data) for building an attribute on-demand on first use.    """    def __init__(self, factory):        """        <factory> is called such: factory(instance) to build the attribute.        """        self._attr_name = factory.__name__        self._factory = factory    def __get__(self, instance, owner):        # Build the attribute.        attr = self._factory(instance)        # Cache the value; hide ourselves.        setattr(instance, self._attr_name, attr)        return attr

Here's how you'd use it:

class Spam(object):    @cached_property    def eggs(self):        print 'long calculation here'        return 6*2s = Spam()s.eggs      # Calculates the value.s.eggs      # Uses cached value.


The usual way would be to make the attribute a property and store the value the first time it is calculated

import timeclass Foo(object):    def __init__(self):        self._bar = None    @property    def bar(self):        if self._bar is None:            print "starting long calculation"            time.sleep(5)            self._bar = 2*2            print "finished long caclulation"        return self._barfoo=Foo()print "Accessing foo.bar"print foo.barprint "Accessing foo.bar"print foo.bar