可重置的 Python 备忘装饰器(针对某个实例)
这个问题是对python 可重置的实例方法缓存装饰器答案的进一步讨论。实际上,我本来想把这个写成对那个答案的评论,但我现在的声望还不够(希望以后能有)。
在那个答案中,@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__
方法来实现相同的效果。
在 __get__(self, obj, objtype)
方法中添加 self.original_self = obj
,然后在 _reset
方法中把 if cached[0] == instance
替换为 if cached[0] == self.original_self
。这样就解决了问题!
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()
你可以使用方法的 __self__
属性(在这里就是 self.__self__
)来找到它绑定的类实例,这样就不用再手动传递这个实例了。