[Python-ideas] caching properties

Scott David Daniels Scott.Daniels at Acm.Org
Thu Apr 30 21:22:48 CEST 2009


Steven D'Aprano wrote:
> Here's a quick&dirty but working solution. (Tested under Python 2.5.)
> 
> def once(func):
>     class Cache(object):
>         def __call__(self, *args, **kwargs):
>             try:
>                 return self._value
>             except AttributeError:
>                 result = func(*args, **kwargs)
>                 self._value = result
>                 return result
>         def expire(self):
>             del self._value
>     return Cache()

This is slightly better (name change as in Antoine Pitrou's comment):

     class cached(object):

         def __init__(self, function):
             self._function = function
             self._cache = {}

         def __call__(self, *args):
             try:
                 return self._cache[args]
             except KeyError:
                 self._cache[args] = self._function(*args)
                 return self._cache[args]

         def expire(self, *args):
             del self._cache[args]


> 
> class Test(object):
>     @property
>     @once
>     def expensive(self):
>         import time
>         time.sleep(20)
>         return 1

Similarly, I can use:
   class Test(object):
       @property
       @cached
       def expensive(self):
           import time
           time.sleep(20)
           return 1

     t = Test()
and use:
     t.expensive

> Works like a charm :) (At least for a solution I knocked up in 30 
> seconds.) The only downside is that to expire the cache, you need the 
> not-very-obvious call:
> 
> Test.expensive.fget.expire()

I'll bet you cannot quite do that; I need to do:
     Test.expensive.fget.expire(t)

Note that 'cached' can do multi-arg functions, though it
doesn't handle kwargs, since there is some ambiguity
between calling keyword-provided args and the args vector.
The change to cache redundant uses would be something like:

-        def __call__(self, *args):
+        def __call__(self, *args, **kwargs):
+            args = args, tuple(sorted(kwargs.items())):

-        def expire(self, *args):
+        def expire(self, *args, **kwargs)):
+            args = args, tuple(sorted(kwargs.items())):

However, this might be fairly counter-intuitive (specifically
when expiring some values, and thinking you have them all).

--Scott David Daniels
Scott.Daniels at Acm.Org




More information about the Python-ideas mailing list