用于选择性缓存/记忆的装饰器
Decorators for selective caching / memoization
我正在寻找一种构建装饰器 @memoize
的方法,我可以在函数中使用它,如下所示:
@memoize
my_function(a, b, c):
# Do stuff
# result may not always be the same for fixed (a,b,c)
return result
那么,如果我这样做:
result1 = my_function(a=1,b=2,c=3)
# The function f runs (slow). We cache the result for later
result2 = my_function(a=1, b=2, c=3)
# The decorator reads the cache and returns the result (fast)
现在说我想强制更新缓存:
result3 = my_function(a=1, b=2, c=3, force_update=True)
# The function runs *again* for values a, b, and c.
result4 = my_function(a=1, b=2, c=3)
# We read the cache
在上面的最后,我们总是有result4 = result3
,但不一定result4 = result
,这就是为什么需要一个选项来强制更新相同输入参数的缓存。
我该如何解决这个问题?
注意 joblib
据我所知joblib
supports .call
, which forces a re-run, but it does not update the cache。
后续使用 klepto
:
有没有办法让 klepto
(参见@Wally 的回答)默认将其结果缓存在特定位置? (例如 /some/path/
)并在多个函数之间共享此位置?例如。我想说
cache_path = "/some/path/"
然后 @memoize
同一路径下给定模块中的几个函数。
我建议查看 joblib
和 klepto
。两者都有非常可配置的缓存算法,可以做你想做的事。
两者都可以为 result1
和 result2
进行缓存,并且 klepto
提供对缓存的访问,因此可以 pop
来自本地内存的结果缓存(不从存储的存档中删除它,比如在数据库中)。
>>> import klepto
>>> from klepto import lru_cache as memoize
>>> from klepto.keymaps import hashmap
>>> hasher = hashmap(algorithm='md5')
>>> @memoize(keymap=hasher)
... def squared(x):
... print("called")
... return x**2
...
>>> squared(1)
called
1
>>> squared(2)
called
4
>>> squared(3)
called
9
>>> squared(2)
4
>>>
>>> cache = squared.__cache__()
>>> # delete the 'key' for x=2
>>> cache.pop(squared.key(2))
4
>>> squared(2)
called
4
不完全是您正在寻找的关键字界面,但它具有您正在寻找的功能。
你可以这样做:
import cPickle
def memoize(func):
cache = {}
def decorator(*args, **kwargs):
force_update = kwargs.pop('force_update', None)
key = cPickle.dumps((args, kwargs))
if force_update or key not in cache:
res = func(*args, **kwargs)
cache[key] = res
else:
res = cache[key]
return res
return decorator
装饰器接受额外的参数force_update
(你不需要在你的函数中声明它)。它从 kwargs
中弹出。所以如果你没有用这些参数调用函数或者你传递 force_update = True
函数将被调用:
@memoize
def f(a=0, b=0, c=0):
import random
return [a, b, c, random.randint(1, 10)]
>>> print f(a=1, b=2, c=3)
[1, 2, 3, 9]
>>> print f(a=1, b=2, c=3) # value will be taken from the cache
[1, 2, 3, 9]
>>> print f(a=1, b=2, c=3, force_update=True)
[1, 2, 3, 2]
>>> print f(a=1, b=2, c=3) # value will be taken from the cache as well
[1, 2, 3, 2]
如果你想自己做:
def memoize(func):
cache = {}
def cacher(a, b, c, force_update=False):
if force_update or (a, b, c) not in cache:
cache[(a, b, c)] = func(a, b, c)
return cache[(a, b, c)]
return cacher
这纯粹是关于 klepto
的后续问题…
流动将扩展@Wally 的示例以指定目录:
>>> import klepto
>>> from klepto import lru_cache as memoize
>>> from klepto.keymaps import hashmap
>>> from klepto.archives import dir_archive
>>> hasher = hashmap(algorithm='md5')
>>> dir_cache = dir_archive('/tmp/some/path/squared')
>>> dir_cache2 = dir_archive('/tmp/some/path/tripled')
>>> @memoize(keymap=hasher, cache=dir_cache)
... def squared(x):
... print("called")
... return x**2
>>>
>>> @memoize(keymap=hasher, cache=dir_cache2)
... def tripled(x):
... print('called')
... return 3*x
>>>
您也可以使用 file_archive
,将路径指定为:
cache = file_archive('/tmp/some/path/file.py')
我正在寻找一种构建装饰器 @memoize
的方法,我可以在函数中使用它,如下所示:
@memoize
my_function(a, b, c):
# Do stuff
# result may not always be the same for fixed (a,b,c)
return result
那么,如果我这样做:
result1 = my_function(a=1,b=2,c=3)
# The function f runs (slow). We cache the result for later
result2 = my_function(a=1, b=2, c=3)
# The decorator reads the cache and returns the result (fast)
现在说我想强制更新缓存:
result3 = my_function(a=1, b=2, c=3, force_update=True)
# The function runs *again* for values a, b, and c.
result4 = my_function(a=1, b=2, c=3)
# We read the cache
在上面的最后,我们总是有result4 = result3
,但不一定result4 = result
,这就是为什么需要一个选项来强制更新相同输入参数的缓存。
我该如何解决这个问题?
注意 joblib
据我所知joblib
supports .call
, which forces a re-run, but it does not update the cache。
后续使用 klepto
:
有没有办法让 klepto
(参见@Wally 的回答)默认将其结果缓存在特定位置? (例如 /some/path/
)并在多个函数之间共享此位置?例如。我想说
cache_path = "/some/path/"
然后 @memoize
同一路径下给定模块中的几个函数。
我建议查看 joblib
和 klepto
。两者都有非常可配置的缓存算法,可以做你想做的事。
两者都可以为 result1
和 result2
进行缓存,并且 klepto
提供对缓存的访问,因此可以 pop
来自本地内存的结果缓存(不从存储的存档中删除它,比如在数据库中)。
>>> import klepto
>>> from klepto import lru_cache as memoize
>>> from klepto.keymaps import hashmap
>>> hasher = hashmap(algorithm='md5')
>>> @memoize(keymap=hasher)
... def squared(x):
... print("called")
... return x**2
...
>>> squared(1)
called
1
>>> squared(2)
called
4
>>> squared(3)
called
9
>>> squared(2)
4
>>>
>>> cache = squared.__cache__()
>>> # delete the 'key' for x=2
>>> cache.pop(squared.key(2))
4
>>> squared(2)
called
4
不完全是您正在寻找的关键字界面,但它具有您正在寻找的功能。
你可以这样做:
import cPickle
def memoize(func):
cache = {}
def decorator(*args, **kwargs):
force_update = kwargs.pop('force_update', None)
key = cPickle.dumps((args, kwargs))
if force_update or key not in cache:
res = func(*args, **kwargs)
cache[key] = res
else:
res = cache[key]
return res
return decorator
装饰器接受额外的参数force_update
(你不需要在你的函数中声明它)。它从 kwargs
中弹出。所以如果你没有用这些参数调用函数或者你传递 force_update = True
函数将被调用:
@memoize
def f(a=0, b=0, c=0):
import random
return [a, b, c, random.randint(1, 10)]
>>> print f(a=1, b=2, c=3)
[1, 2, 3, 9]
>>> print f(a=1, b=2, c=3) # value will be taken from the cache
[1, 2, 3, 9]
>>> print f(a=1, b=2, c=3, force_update=True)
[1, 2, 3, 2]
>>> print f(a=1, b=2, c=3) # value will be taken from the cache as well
[1, 2, 3, 2]
如果你想自己做:
def memoize(func):
cache = {}
def cacher(a, b, c, force_update=False):
if force_update or (a, b, c) not in cache:
cache[(a, b, c)] = func(a, b, c)
return cache[(a, b, c)]
return cacher
这纯粹是关于 klepto
的后续问题…
流动将扩展@Wally 的示例以指定目录:
>>> import klepto
>>> from klepto import lru_cache as memoize
>>> from klepto.keymaps import hashmap
>>> from klepto.archives import dir_archive
>>> hasher = hashmap(algorithm='md5')
>>> dir_cache = dir_archive('/tmp/some/path/squared')
>>> dir_cache2 = dir_archive('/tmp/some/path/tripled')
>>> @memoize(keymap=hasher, cache=dir_cache)
... def squared(x):
... print("called")
... return x**2
>>>
>>> @memoize(keymap=hasher, cache=dir_cache2)
... def tripled(x):
... print('called')
... return 3*x
>>>
您也可以使用 file_archive
,将路径指定为:
cache = file_archive('/tmp/some/path/file.py')