python resettable memoization装饰器(用于某个实例)

时间:2011-12-23 18:52:56

标签: python decorator memoization

这个问题是python resettable instance method memoization decorator答案的后续问题。 事实上,我会将此作为对该答案的评论,但我没有(但我希望)有足够的声誉。

在那个答案中,@ aix提供了一种使用装饰器重置记忆函数的好方法。

该答案的“问题”是,为某个装饰方法调用reset会重置所有实例的缓存。使用@aix定义的相同类的示例应该清楚说明:

c = my_class()
print c.my_func(55)
# This time the function is computed and stored in cache
print c.my_func(55)
# This second call is cached...  no computation needed
d = my_class()
d.my_func.reset()
print c.my_func(55)
# This third call is also computed, since the cache has been cleared

我认为d.my_func.reset()应该只清除d.my_func的预先计算值的缓存,而不是my_class的所有其他实例。

我有一个半解决方案,不能完全说服,但我想有人可以改进。

我修改了reset()方法并引入了参数instance

  def _reset(self,instance):
  for cached in self.cache.keys():
      if cached[0] == instance:
          del self.cache[cached] 

现在,如果我这样做:

c = my_class()
print c.my_func(55)
# This time the function is computed and stored in cache
print c.my_func(55)
# This second call is cached
d = my_class()
d.my_func.reset(d)
print c.my_func(55)
# Now this third call is cached

然而,调用重置方法的方式:d.my_func.reset(d)似乎(至少)难看,但我找不到更好的解决方案......有没有人有想法?

谢谢!

修改

对于记录:不是将实例作为参数传递,而是可以在修改装饰器的__get__方法时获得相同的行为。

self.original_self = obj方法中添加__get__(self, obj, objtype),并在if cached[0] == instance方法中将if cached[0] == self.original_self替换为_reset。这解决了这个问题!

2 个答案:

答案 0 :(得分:0)

您可以使用方法的__self__属性(在本例中为self.__self__)来查找它所绑定的类实例(而不是必须传递实例) )。

答案 1 :(得分:0)

2种可能性-也存储在实例__dict__内,而不是全局备忘单:

def MemoMeth(meth):
    """Memoizer for class methods (uses instance __dict__)
    Example:
    class C:
        @MemoMeth
        def slowmeth(self, a, b): ...
    """
    class _MemoMeth:
        def __init__(self, this):
            self.this = this
        def __call__(self, *args):
            memo = self.this.__dict__.setdefault(_MemoMeth, {})
            try:
                return memo[args][1]
            except:
                memo[args] = tr = time.time(), meth(self.this, *args)
                return tr[1]
        def reset(self):
            self.this.__dict__.setdefault(_MemoMeth, {}).clear()
    return property(_MemoMeth)

此操作每次访问使用线程本地存储而不是新的包装对象:

def MemoMeth3(meth):
    """Memoizer for class methods (uses instance __dict__)
    Example:
    class C:
        @MemoMeth3
        def slowmeth(self, a, b): ...
    """
    tls = threading.local()
    class MemoProperty(object):
        def __call__(self, *args):
            memo = tls.instance.__dict__.setdefault(self, {})
            try:
                return memo[args][1]
            except:
                memo[args] = tr = time.time(), meth(tls.instance, *args)
                return tr[1]
        def reset(self):
            tls.instance.__dict__.setdefault(self, {}).clear()
        def __get__(self, instance, owner):
            tls.instance = instance
            return self
    return MemoProperty()