Python装饰器、类方法和评估——django memoize
我有一个正在工作的memoize decorator,它使用Django的缓存后端在一定时间内记住函数的结果。我专门将其应用于一个类方法 我的装饰师看起来像:Python装饰器、类方法和评估——django memoize,python,class,scope,methods,decorator,Python,Class,Scope,Methods,Decorator,我有一个正在工作的memoize decorator,它使用Django的缓存后端在一定时间内记住函数的结果。我专门将其应用于一个类方法 我的装饰师看起来像: def memoize(prefix='mysite', timeout=300, keygenfunc=None): # MUST SPECIFY A KEYGENFUNC(args, kwargs) WHICH MUST RETURN A STRING def funcwrap(meth): def ke
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”不会被传入,因此无法生成密钥。如前所示,我得到一个指向lambda函数的“IndexError:tuple index out of range”错误
当然,我可以成功地拨打:
myStorageUnitInstance.someBigCalculation.invalidate(myStorageUnitInstance)
这是完美的。但当我已经在引用一个特定的实例时,它“感觉”是多余的。如何使Python将其视为实例绑定方法,从而正确地填充“self”变量?描述符必须始终在类上设置,而不是在实例上设置(有关所有详细信息,请参阅)。当然,在本例中,您甚至没有在实例上设置它,而是在另一个函数上设置它(并将其作为绑定方法的属性获取)。我认为使用您想要的语法的唯一方法是使funcwrap成为自定义类的实例(当然,该类必须是描述符类,即定义适当的
\uuuuuuu get\uuuu
方法,就像函数本身一样)。然后,invalidate
可以是该类的一个方法(或者更好的是,另一个自定义类,其实例是前面提到的描述符类的\uu get\uuuu
方法生成的“绑定方法类物质”),并最终达到您所渴望的im\u self
(在绑定方法中是这样命名的)
这是一个相当大的代价(概念和编码;-)来为你所寻求的小小的便利付出——足够大,以至于我真的不想花一两个小时来完全开发和测试它。但我希望我已经给了你足够明确的指示,如果你仍然热衷于此,我会很高兴地澄清并帮助你,如果在这一过程中有任何不清楚的地方或有什么阻碍你的话。虽然我同意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)
更改为(实例)
,因为这就是您在示例中使用它的方式(如果从方法调用的参数而不是对象实例生成一个键,则不可能使用someBigCalculation.invalidate
方法以您想要的方式清除缓存)
这个代码中有很多东西,所以它是否真的让你的生活更容易被考虑。
class StorageUnit(models.Model):
@memoize(timeout=60*180, keygenfunc=lambda x: str(x.id))
def someBigCalculation(self):
return 'big calculation'