Python 装饰器和类方法和评估——django memoize

Posted

技术标签:

【中文标题】Python 装饰器和类方法和评估——django memoize【英文标题】:Python decorators and class methods and evaluation -- django memoize 【发布时间】:2010-12-15 17:26:36 【问题描述】:

我有一个可用的 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”没有被传入,因此密钥没有被生成。如前所示,我收到指向我的 lambda 函数的“IndexError: tuple index out of range”错误。

当然可以成功调用:

myStorageUnitInstance.someBigCalculation.invalidate(myStorageUnitInstance)

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

【问题讨论】:

【参考方案1】:

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

一个相当高的(概念和编码;-)为你寻求的小便利付出的代价——高到我真的不想花一两个小时来完全开发和测试它。但是,如果您仍然热衷于此,我希望我已经为您提供了足够明确的指示,以便您继续进行,如果有任何不清楚的地方或阻碍您前进的事情,我确实很乐意澄清并提供帮助。

【讨论】:

【参考方案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 方法以这种方式清除缓存如果你从方法调用的参数而不是对象实例生成一个键,你会想要)。

class StorageUnit(models.Model):
    @memoize(timeout=60*180, keygenfunc=lambda x: str(x.id))
    def someBigCalculation(self):
        return 'big calculation'

该代码中有很多内容,因此需要考虑它是否真的让您的生活更轻松。

【讨论】:

以上是关于Python 装饰器和类方法和评估——django memoize的主要内容,如果未能解决你的问题,请参考以下文章

函数装饰器和类装饰器实现单例类

python进阶强化学习

Python(pandas):通过pandas API使用装饰器

Django:(05)类视图,装饰器和中间件

python -- 迭代器和装饰器

Python的静态方法和类方法