我有一个带有两个参数的方法,可以进行一些复杂的计算。它通常使用相同的参数调用,因此我使用字典进行缓存。目前看起来像这样:
def foo(self, a, b):
params = frozenset([a, b])
if not params in self._cache:
self._cache[params] = self._calculate(a, b)
return self._cache[params]
构建frozenset
的原因是参数可以是任何顺序,但结果将是相同的。我想知道是否有更简单(最重要的是更有效)的解决方案。
答案 0 :(得分:2)
关于如何实现缓存,没有什么特别低效或复杂的事情;这基本上是需要发生的事情。然而,它不是非常一般。
为方便起见,您可以实现某种更通用的缓存策略,如果您愿意,可以使用装饰器。一种可能的方法可能是:
class Memoizer(object):
def __init__(self):
self._cache = dict()
def memoize_unordered(self, f):
def wrapper(s, *args, **kwargs):
key = (s, f, frozenset(args), frozenset(kwargs.iteritems()))
if key not in self._cache:
print 'calculating', args, kwargs
self._cache[key] = f(s, *args, **kwargs)
return self._cache[key]
return wrapper
def memoize_ordered(self, f):
def wrapper(s, *args, **kwargs):
key = (s, f, tuple(args), frozenset(kwargs.iteritems()))
if key not in self._cache:
print 'calculating', args, kwargs
self._cache[key] = f(s, *args, **kwargs)
return self._cache[key]
return wrapper
memoizer = Memoizer()
class Foo(object):
@memoizer.memoize_unordered
def foo(self, a, b):
return self._calculate(a, b)
def _calculate(self, a, b):
return frozenset([a,b])
foo = Foo()
results = [foo.foo(*a) for a in [(1, 5), (1, 5), (5, 1), (9, 12), (12, 9)]]
for result in results:
print 'RESULT', result
打印:
calculating (1, 5) {}
calculating (9, 12) {}
RESULT frozenset([1, 5])
RESULT frozenset([1, 5])
RESULT frozenset([1, 5])
RESULT frozenset([9, 12])
RESULT frozenset([9, 12])
当然,在对象之外实现缓存的缺点是,当对象消失时,缓存数据不会被删除,除非你小心翼翼地实现这一点。
答案 1 :(得分:0)
您可以将两个值组合成一个哈希值,如str(a)+'\ 0'+ str(b)然后将其放入缓存中,或者您可以创建一个二维数组以便缓存[ a] [b]返回你想要的值。
您可能还希望将此功能转换为@decorator类型的函数,然后您可以在多个函数上重用装饰器并为缓存维护一个代码位置。
答案 2 :(得分:0)
我只是将它存储在缓存中两次,每次订购一次。
def foo(self, a, b):
try:
return self._cache[(a, b)]
except KeyError:
value = self._calculate(a, b)
self._cache[(a, b)] = self._cache[(b, a)] = value
return value
答案 3 :(得分:0)
你可以使用beaker.cache(http://beaker.groovie.org/index.html)
# define a cache manager
cache = CacheManager(dict_of_config_options)
# in your class, apply a cache decorator
@cache.cache('mycache')
def foo(self, a,b):
return self._calculate
我认为默认情况下它的工作方式与你想要的一样。不确定这是否将self用作密钥的一部分。它假定a,b是可选择的。
答案 4 :(得分:0)
您的代码有两个问题:
(1)它使用旧的dict.has_key()方法,该方法很慢并且已经在Python 3.x中消失了。而是使用“键盘中的键”或“不键盘中的键”。
所以:
def foo(self, a, b):
params = frozenset([a, b])
if params in self._cache:
self._cache[params] = self._calculate(a, b)
return self._cache[params]
(2)“dict中的键”更具可读性,并且暴露了更糟糕的问题:你的代码不起作用!如果args在dict中,它会重新计算。如果它们不在dict中,它将会出现KeyError。请考虑复制/粘贴,而不是从内存中键入。
所以:
def foo(self, a, b):
params = frozenset([a, b])
if params not in self._cache:
self._cache[params] = self._calculate(a, b)
return self._cache[params]
(3)更多效率建议:
def foo(self, a, b):
if a < b:
params = (a, b)
else:
params = (b, a)
try:
return self._cache[params]
except KeyError:
v = self._cache[params] = self._calculate(a, b)
return v