我有一个可用的 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”没有被传入,因此 key 也没有被生成。我收到一个“IndexError:元组索引超出范围”错误,指向我的 lambda 函数,如前所示。
当然可以成功调用:
myStorageUnitInstance.someBigCalculation.invalidate(myStorageUnitInstance)
这非常有效。但是当我已经引用了一个特定的实例时,它“感觉”是多余的。我怎样才能使 Python 将其视为实例绑定(bind)方法并因此正确填写“self”变量?
最佳答案
描述符必须始终在类上设置,而不是在实例上设置(有关所有详细信息,请参见 the how-to guide)。当然,在这种情况下,您甚至没有在实例上设置它,而是在另一个函数上设置它(并将其作为绑定(bind)方法的属性获取)。我认为使用你想要的语法的唯一方法是使 funcwrap 成为自定义类的实例(当然,哪个类必须是描述符类,即定义适当的 __get__
方法,就像函数本质上做的那样) .然后 invalidate
可以是该类的一个方法(或者,也许更好,另一个自定义类,其实例是由前面提到的描述符类的 __get__
方法产生的“类似绑定(bind)方法的物质”),并最终到达 im_self
您渴望的(这就是它在绑定(bind)方法中的命名方式)。
为您寻求的次要便利付出相当高的(概念和编码;-)代价 -- 足够高以至于我真的不想花一两个小时来完全开发它并测试它。但我希望我已经给了你足够明确的指示,如果你仍然热衷于此,你可以继续,而且如果有任何不清楚的地方或有什么东西阻碍你前进,我确实很乐意澄清并提供帮助。
关于Python装饰器和类方法及求值——django memoize,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1719527/