A cached property decorator.

Updated Gribouillis 0 Tallied Votes 486 Views Share

This snippet defines a cachedProperty decorator. A cached property differs from a property in that it's value is only computed the first time that the property is accessed and then stored in the object's dict for later use. If the object's attribute is explicitely deleted, it will be computed again the next time it's accessed.

#!/usr/bin/env python
# Copyright (c) Gribouillis at www.daniweb.com
# License: Public Domain
# Use this code freely

from functools import update_wrapper 

def cachedProperty (func ,name =None ):
  """cachedProperty(func, name=None) -> a descriptor
This decorator implements an object's property which is computed
the first time it is accessed, and which value is then stored in
the object's __dict__ for later use. If the attribute is deleted,
the value will be recomputed the next time it is accessed.
  Usage:
    class X(object):
      @cachedProperty
      def foo(self):
        return computation()
"""
  if name is None :
    name =func .__name__ 
  def _get (self ):
    try :
      return self .__dict__ [name ]
    except KeyError :
      value =func (self )
      self .__dict__ [name ]=value 
      return value 
  update_wrapper (_get ,func )
  def _del (self ):
    self .__dict__ .pop (name ,None )
  return property (_get ,None ,_del )

if __name__ =="__main__":
# test code for cachedProperty
  import time 

  class X (object ):
    @cachedProperty 
    def foo (self ):
      print ("--> foo was called")
      return time .asctime ()

  def dictContent (instance ):
    return list (sorted (instance .__dict__ ))

  x =X ()
  print (dictContent (x ))# nothing in x.__dict__
  print (x .foo )# the attribute is computed and stored in x.__dict__
  time .sleep (2 )
  print (dictContent (x ))# x.__dict__ has a key 'foo'
  print (x .foo )# the cached value is used
  print (x .foo )# the cached value is used
  del x .foo # the cached value is deleted
  print (dictContent (x ))# x.__dict__ doesn't have a 'foo'
  print (x .foo )# the attribute is recomputed and stored
  print (dictContent (x ))# x.__dict__ has a 'foo'