
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@Acm.Org