有Python缓存库吗?


123

我正在寻找Python缓存库,但到目前为止找不到任何东西。我需要一个简单dict的类似接口,可以在其中设置密钥及其有效期,并将其重新缓存。有点像:

cache.get(myfunction, duration=300)

它将从缓存中为我提供该项目(如果存在),或者调用该函数并将其存储(如果它不存在或已过期)。有人知道这样吗?


我认为您item在您的榜样中不见了。
SilentGhost

是的,这可能需要一个钥匙...而且,2.x。
Stavros Korokithakis 09年

3
在同一流程中还是在流程之间共享?是否穿线?
亚伦·沃特斯

1
它应该是线程安全的,对不起,我应该提到。我不需要在进程之间共享。
Stavros Korokithakis 09年

6
尝试使用DiskCache:获得Apache2许可,100%覆盖率,线程安全,进程安全,多种逐出策略和快速(基准)
GrantJ

Answers:



72

在Python 3.2中,您可以使用functools库中的装饰器@lru_cache。这是最近使用过的高速缓存,因此其中的项目没有过期时间,但是作为快速破解,它非常有用。

from functools import lru_cache

@lru_cache(maxsize=256)
def f(x):
  return x*x

for x in range(20):
  print f(x)
for x in range(20):
  print f(x)

20
cachetools提供了一个很好的实现这些,它是兼容的Python 2和Python 3
vaab

1
大+1 cachetools ......似乎很酷,有一对夫妇更多的缓存算法:)
乔恩赫斯

绝不建议这样做!保持兼容。
PascalVKooten

1
@roboslone,距离您关于线程不安全的评论有两年(减去4天..)的时间,它可能已更改。我有cachetools 2.0.0,并且在代码中看到它使用RLock。/usr/lib/python2.7/site-packages/cachetools/func.py
Motty

@Motty:cachetools 4.0.0.0 的文档说:“请注意,所有这些类都不是线程安全的。必须正确同步从多个线程访问共享缓存,例如,使用带有合适的对象”(粗体显示)
martineau

28

您还可以查看Memoize装饰器。您可能无需做太多修改就可以使它完成您想做的事情。


那很聪明。进行一些更改,装饰器甚至可以在设置的时间后过期。
Ehtesh Choudhury

您绝对可以对装饰器中的缓存写入基于空间的限制。如果您想要一个函数,例如逐项生成斐波那契数列,那将很有帮助。您想要缓存,但是只需要最后两个值-保存所有值只是空间效率低下。
reem 2013年

14

Joblib https://joblib.readthedocs.io支持Memoize模式中的缓存功能。通常,这种想法是缓存计算上昂贵的功能。

>>> from joblib import Memory
>>> mem = Memory(cachedir='/tmp/joblib')
>>> import numpy as np
>>> square = mem.cache(np.square)
>>> 
>>> a = np.vander(np.arange(3)).astype(np.float)
>>> b = square(a)                                   
________________________________________________________________________________
[Memory] Calling square...
square(array([[ 0.,  0.,  1.],
       [ 1.,  1.,  1.],
       [ 4.,  2.,  1.]]))
___________________________________________________________square - 0...s, 0.0min

>>> c = square(a)

您也可以做一些花哨的事情,例如在函数上使用@ memory.cache装饰器。该文档位于此处:https : //joblib.readthedocs.io/en/latest/generation/joblib.Memory.html


2
附带一提,当您使用大型NumPy数组时,joblib确实会发光,因为它具有专门处理它们的特殊方法。
alexbw 2014年


9

我认为python memcached API是流行的工具,但我自己并未使用过它,也不确定它是否支持您所需的功能。


3
那是行业标准,但是我想要的只是一个简单的内存存储机制,可以容纳100个左右的密钥,而memcached有点过头了。不过,谢谢您的回答。
Stavros Korokithakis 09年

7
import time

class CachedItem(object):
    def __init__(self, key, value, duration=60):
        self.key = key
        self.value = value
        self.duration = duration
        self.timeStamp = time.time()

    def __repr__(self):
        return '<CachedItem {%s:%s} expires at: %s>' % (self.key, self.value, time.time() + self.duration)

class CachedDict(dict):

    def get(self, key, fn, duration):
        if key not in self \
            or self[key].timeStamp + self[key].duration < time.time():
                print 'adding new value'
                o = fn(key)
                self[key] = CachedItem(key, o, duration)
        else:
            print 'loading from cache'

        return self[key].value



if __name__ == '__main__':

    fn = lambda key: 'value of %s  is None' % key

    ci = CachedItem('a', 12)
    print ci 
    cd = CachedDict()
    print cd.get('a', fn, 5)
    time.sleep(2)
    print cd.get('a', fn, 6)
    print cd.get('b', fn, 6)
    time.sleep(2)
    print cd.get('a', fn, 7)
    print cd.get('b', fn, 7)

5
我做了类似的事情,但是您需要用于多线程的锁和一个size参数来避免其无限增长。然后,您需要一些功能来按访问对键进行排序,以丢弃访问最少的键,
依此类推

再版行不正确(应使用self.timeStamp)。同样,这是一个糟糕的实现,不需要为每个get()进行数学运算。到期时间应在CachedItem init中计算。
ivo

1
实际上,如果仅实现get方法,则不应将其作为dict的子类,而应将其作为具有嵌入式dict的对象。
ivo

6

您可以使用我的简单解决方案来解决该问题。这真的很简单,没有花哨:

class MemCache(dict):
    def __init__(self, fn):
        dict.__init__(self)
        self.__fn = fn

    def __getitem__(self, item):
        if item not in self:
            dict.__setitem__(self, item, self.__fn(item))
        return dict.__getitem__(self, item)

mc = MemCache(lambda x: x*x)

for x in xrange(10):
    print mc[x]

for x in xrange(10):
    print mc[x]

它确实缺乏到期功能,但是您可以通过在MemCache c-tor中指定特定规则来轻松扩展它。

希望代码是不言而喻的,但是,如果不是,就更不用说了,高速缓存正在作为其c-tor参数之一传递给翻译函数。依次用于生成有关输入的缓存输出。

希望能帮助到你


1
+1表示简单的建议。根据问题的不同,它可能只是工作的工具。附注:您不需要elsein __getitem__:)
hiwaylon,

为什么他不需要else__getitem__?那就是他填满
辞典

5

尝试使用redis,它是应用程序以原子方式共享数据或如果您具有某种Web服务器平台的最干净,最简单的解决方案之一。它非常容易设置,您将需要一个python redis客户端http://pypi.python.org/pypi/redis


1
应该提到的是,它已经过时,需要使用TCP进行访问。
jeffry copps 18/12/17


2

项目旨在提供“为人类提供缓存”(尽管似乎相当未知)

来自项目页面的一些信息:

安装

点安装缓存

用法:

import pylibmc
from cache import Cache

backend = pylibmc.Client(["127.0.0.1"])

cache = Cache(backend)

@cache("mykey")
def some_expensive_method():
    sleep(10)
    return 42

# writes 42 to the cache
some_expensive_method()

# reads 42 from the cache
some_expensive_method()

# re-calculates and writes 42 to the cache
some_expensive_method.refresh()

# get the cached value or throw an error
# (unless default= was passed to @cache(...))
some_expensive_method.cached()


-5

keyring是最好的python缓存库。您可以使用

keyring.set_password("service","jsonkey",json_res)

json_res= keyring.get_password("service","jsonkey")

json_res= keyring.core.delete_password("service","jsonkey")

那是一个密钥环库,而不是一个缓存库。
Stavros Korokithakis

@StavrosKorokithakis其实,我实现了通过钥匙圈缓存键
小鬼
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.