Python装饰器和类方法与评估Django memoiz

2024-05-20 22:03:57 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个工作的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 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

我在一个类方法上使用这个方法,所以类似于:

^{pr2}$

实际的记忆过程非常完美!也就是说,打电话给

myStorageUnitInstance.someBigCalculation()

正确使用缓存。好吧,酷!在

我的问题是,当我试图手动使一个特定实例的条目失效时,我想在这个实例中运行

myStorageUnitInstance.someBigCalculation.invalidate()

但是,这不起作用,因为“self”不能被传递,因此密钥也不能被生成。“索引超出我的索引范围”显示为“LamberDa”错误。在

当然,我可以成功地拨打:

myStorageUnitInstance.someBigCalculation.invalidate(myStorageUnitInstance)

这很好用。但当我已经在引用一个特定的实例时,它“感觉”是多余的。如何让Python将其视为实例绑定的方法,从而正确地填充“self”变量?在


Tags: 实例方法keynonecachereturndefargs
2条回答

虽然我同意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方法不可能以您想要的方式清除缓存)。在

^{pr2}$ 这个代码中有很多东西,所以它是否真的让你的生活更容易被考虑。在

必须始终在类上设置描述符,而不是在实例上设置描述符(有关所有详细信息,请参见the how-to guide)。当然,在本例中,您甚至不需要在实例上设置它,而是在另一个函数上设置它(并将其作为绑定方法的属性获取)。我认为使用语法的唯一方法是使funcwrap成为自定义类的实例(当然,该类必须是描述符类,即定义适当的__get__方法,就像函数本质上那样)。那么invalidate可以是该类的一个方法(或者,也许更好的是另一个自定义类,其实例是前面提到的描述符类的__get__方法生成的“绑定方法类实体”),并最终到达您所渴望的im_self(这就是它在绑定方法中的命名方式)。在

这是一个相当大的代价(概念和编码;-)为你所寻求的小便利付出的巨大代价,我真的不想花一两个小时来完全开发和测试它。但我希望我已经给了你足够明确的指示,让你继续做下去,如果你仍然热衷于这一点,我很乐意澄清和帮助你,如果有什么不清楚的东西或是什么东西阻碍了你的进展。在

相关问题 更多 >