[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