Welcome, guest | Sign In | My Account | Store | Cart

A cached property is a read-only property that is calculated on demand and automatically cached. If the value has already been calculated, the cached value is returned.

Python, 14 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
def cached_property(f):
    """returns a cached property that is calculated by function f"""
    def get(self):
        try:
            return self._property_cache[f]
        except AttributeError:
            self._property_cache = {}
            x = self._property_cache[f] = f(self)
            return x
        except KeyError:
            x = self._property_cache[f] = f(self)
            return x
        
    return property(get)

Example:

class Swallow:
   
def __init__(self, laden):
       
self.mass = 5.0
       
self.laden = laden

   
def calc_air_speed_velocity(self):
        coconut_mass
= 16.0
        t0
= time.time()
        mass
= self.mass + (coconut_mass if self.laden else 0.0)
        distance
= 43
        time
.sleep(mass*distance/1000.0) # must sleep after flying
        t
= time.time() - t0
       
return distance/t

    air_speed_velocity
= cached_property(calc_air_speed_velocity)

s1
= Swallow(False)
s2
= Swallow(True)

print s1.air_speed_velocity
print s2.air_speed_velocity

print s1.air_speed_velocity  # notice that the second two invocations do not take any time.
print s2.air_speed_velocity

4 comments

David Lambert 15 years, 5 months ago  # | flag

It works as a decorator.

class Swallow:

   
def __init__(self, laden):
       
self.mass = 5.0
       
self.laden = laden

   
@cached_property
   
def air_speed(self):
        coconut_mass
= 16.0
        t0
= time.time()
        mass
= self.mass + (coconut_mass if self.laden else 0.0)
        distance
= 43
        time
.sleep(mass*distance/400.0) # must sleep after flying
        t
= time.time() - t0
       
return distance/t
runsun pan 14 years, 12 months ago  # | flag

This might be of interest:

Easy Property Creation in Python http://code.activestate.com/recipes/576742/

Only 7 lines of code, easy to understand, easy to use, easy to customize, save you a lot of typing

Giampaolo Rodolà 10 years, 4 months ago  # | flag

Thanks for writing this. Here's a slightly modified version using functools.wraps() which preserves the original property docstring and name:

import functools

def cached_property(fun):
   
"""A memoize decorator for class properties."""
   
@functools.wraps(fun)
   
def get(self):
       
try:
           
return self._cache[fun]
       
except AttributeError:
           
self._cache = {}
       
except KeyError:
           
pass
        ret
= self._cache[fun] = fun(self)
       
return ret
   
return property(get)
Andras Gefferth 9 years, 12 months ago  # | flag

Another modification/improvement: you can define member variable names, whose change should trigger the recalculation of the cached value.

def smart_cached_property(inputs=[]):
   
def smart_cp(f):
       
@functools.wraps(f)
       
def get(self):
            input_values
= dict((key,getattr(self,key)) for key in inputs )
           
try:
                x
= self._property_cache[f]
               
if input_values == self._property_input_cache[f]:
                   
return x
           
except AttributeError:
               
self._property_cache ={}
               
self._property_input_cache = {}
           
except KeyError:
               
pass

            x
= self._property_cache[f] = f(self)
           
self._property_input_cache[f] = input_values

           
return x
       
return property(get)
   
return smart_cp

Usage example:

@smart_cached_property(['mass','laden'])
def air_speed_velocity(self):
   
...
   
...
Created by Ken Seehart on Thu, 13 Nov 2008 (MIT)
Python recipes (4591)
Ken Seehart's recipes (1)

Required Modules

  • (none specified)

Other Information and Tasks