在Python中使用两个参数缓存函数的结果

时间:2010-07-08 19:35:33

标签: python caching

我有一个带有两个参数的方法,可以进行一些复杂的计算。它通常使用相同的参数调用,因此我使用字典进行缓存。目前看起来像这样:

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的原因是参数可以是任何顺序,但结果将是相同的。我想知道是否有更简单(最重要的是更有效)的解决方案。

5 个答案:

答案 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