I have a working memoize decorator which uses Django's cache backend to remember the result of a function for a certain amount of time. I am specifically applying this to a class method.
My decorator looks like:
def memoize(prefix='mysite', timeout=300, keygenfunc=None):
# MUST SPECIFY A KEYGENFUNC(args, kwargs) WHICH MUST RETURN A STRING
def funcwrap(meth):
def keymaker(*args, **kwargs):
key = prefix + '___' + meth.func_name + '___' + keygenfunc(args, kwargs)
return key
def invalidate(*args, **kwargs):
key = keymaker(*args, **kwargs)
cache.set(key, None, 1)
def newfunc(*args, **kwargs):
# construct key
key = keymaker(*args, **kwargs)
# is in cache?
rv = cache.get(key)
if rv is None:
# cache miss
rv = meth(*args, **kwargs)
cache.set(key, rv, timeout)
return rv
newfunc.invalidate = invalidate
return newfunc
return funcwrap
I am using this on a class method, so something like:
class StorageUnit(models.Model):
@memoize(timeout=60*180, keygenfunc=lambda x,y: str(x[0].id))
def someBigCalculation(self):
...
return result
The actual memoization process works perfectly! That is, a call to
myStorageUnitInstance.someBigCalculation()
properly uses the cache. OK, cool!
My problem is when I try to manually invalidate the entry for a specific instance, where I want to be able to run
myStorageUnitInstance.someBigCalculation.invalidate()
However, this doesn't work, because "self" doesn't get passed in and therefore the key doesn't get made. I get a "IndexError: tuple index out of range" error pointing to my lambda function as shown earlier.
Of course, I can successfully call:
myStorageUnitInstance.someBigCalculation.invalidate(myStorageUnitInstance)
and this works perfectly. But it "feels" redundant when I'm already referencing a specific instance. How can I make Python treat this as an instance-bound method and therefore properly fill in the "self" variable?