Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/300.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python装饰器、类方法和评估——django memoize_Python_Class_Scope_Methods_Decorator - Fatal编程技术网

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

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

我有一个正在工作的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
我在一个类方法上使用它,所以类似于:

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'