我有一个工作的memoize装饰器,它使用Django的缓存后端来记住函数的结果一段时间。我特意将它应用于类方法。
我的装饰师看起来像:
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
我在类方法上使用它,所以类似于:
class StorageUnit(models.Model):
@memoize(timeout=60*180, keygenfunc=lambda x,y: str(x[0].id))
def someBigCalculation(self):
...
return result
实际的记忆过程完美无缺!也就是说,打电话给
myStorageUnitInstance.someBigCalculation()
正确使用缓存。好的,很酷!
我的问题是当我尝试手动使特定实例的条目无效时,我希望能够运行
myStorageUnitInstance.someBigCalculation.invalidate()
然而,这不起作用,因为“self”不会被传入,因此密钥不会被创建。我得到一个“IndexError:tuple index out of range”错误指向我的lambda函数,如前所示。
当然,我可以成功致电:
myStorageUnitInstance.someBigCalculation.invalidate(myStorageUnitInstance)
这完美无缺。但是当我已经引用特定实例时,它“感觉”是多余的。如何让Python将其视为实例绑定方法,从而正确填写“self”变量?
答案 0 :(得分:2)
必须始终在类上设置描述符,而不是在实例上设置描述符(有关所有详细信息,请参阅the how-to guide)。当然,在这种情况下,您甚至不在实例上设置它,而是在另一个函数上设置它(并将其作为绑定方法的属性获取)。我认为使用你想要的语法的唯一方法是使funcwrap成为自定义类的实例(当然,哪个类必须是描述符类,即定义适当的__get__
方法,就像函数本身一样做)。然后invalidate
可以是该类的一个方法(或者更好的是,另一个自定义类,其实例是由前面提到的描述符类__get__
方法生成的“绑定方法类物质”),并最终达到你渴望的im_self
(这就是它在命名方法中命名的方式)。
为你寻求的轻微便利付出相当大的代价(概念和编码;-)价格 - 足够大,我真的不想花一两个小时完全开发它并测试它。但是,如果你仍然热衷于此,我希望我已经给你足够明确的指示让你继续前进,事实上,如果有什么不清楚或者有什么事情让你难以理解,我会很乐意澄清并帮助你。
答案 1 :(得分:-1)
虽然我同意AlexM,但我确实有空闲时间,并认为这会很有趣:
# from django.whereever import cache
class memoize(object):
def __init__(self,prefix='mysite', timeout=300, keygenfunc=None):
class memo_descriptor(object):
def __init__(self,func):
self.func = func
def __get__(self,obj,klass=None):
key = prefix + '___' + self.func.func_name + '___' + keygenfunc(obj)
class memo(object):
def __call__(s,*args,**kwargs):
rv = cache.get(key)
if rv is None:
rv = self.func(obj,*args, **kwargs)
cache.set(key, rv, timeout)
return rv
def invalidate(self):
cache.set(key, None, 1)
return memo()
self.descriptor = memo_descriptor
def __call__(self,func):
return self.descriptor(func)
注意我已将keygenfunc
签名从(*args,**kwargs)
更改为(instance)
,因为这是您在示例中使用它的方式(并且无法拥有someBigCalculation.invalidate
如果从方法调用的参数而不是对象实例生成密钥,则以您希望的方式清除缓存。
class StorageUnit(models.Model):
@memoize(timeout=60*180, keygenfunc=lambda x: str(x.id))
def someBigCalculation(self):
return 'big calculation'
该代码中存在很多内容,因此需要考虑的是它是否真正让您的生活更轻松。