【问题标题】:Store the cache to a file functools.lru_cache in Python >= 3.2在 Python >= 3.2 中将缓存存储到文件 functools.lru_cache
【发布时间】:2013-03-13 04:53:42
【问题描述】:

我在 Python 3.3 中使用 @functools.lru_cache。我想将缓存保存到一个文件中,以便在程序重新启动时恢复它。我该怎么办?

编辑 1 可能的解决方案:We need to pickle any sort of callable

酸洗问题__closure__:

_pickle.PicklingError: Can't pickle <class 'cell'>: attribute lookup builtins.cell failed

如果我尝试在没有它的情况下恢复功能,我会得到:

TypeError: arg 5 (closure) must be tuple

【问题讨论】:

  • 请注意,我认为 LRU 缓存实现将被 Python 3.4 或 3.5 中的 C 实现取代,任何提取缓存内容的尝试都可能不会面向未来。跨度>
  • @MartijnPieters:感谢您提供的信息。
  • 避免lru_cache。拥有lru_cache 对您的函数重要还是一个简单的缓存就足够了?否则你可以重新实现lru_cache 并添加你想要的功能。
  • @Bakuriu:一个简单的缓存就足够了。我找到了 lru_cache,我在问自己是否可以保存它的状态。

标签: python python-3.x memoization


【解决方案1】:

你不能使用lru_cache 做你想做的事,因为它不提供访问缓存的 API,而且它可能在未来的版本中用 C 重写。如果你真的想保存缓存,你必须使用不同的解决方案来访问缓存。

自己写一个缓存很简单。例如:

from functools import wraps

def cached(func):
    func.cache = {}
    @wraps(func)
    def wrapper(*args):
        try:
            return func.cache[args]
        except KeyError:
            func.cache[args] = result = func(*args)
            return result   
    return wrapper

然后您可以将其用作装饰器:

>>> @cached
... def fibonacci(n):
...     if n < 2:
...             return n
...     return fibonacci(n-1) + fibonacci(n-2)
... 
>>> fibonacci(100)
354224848179261915075L

并检索cache

>>> fibonacci.cache
{(32,): 2178309, (23,): 28657, ... }

然后您可以随意腌制/取消腌制缓存并加载它:

fibonacci.cache = pickle.load(cache_file_object)

我在 python 的问题跟踪器中找到了一个 feature request,用于将转储/加载添加到 lru_cache,但它未被接受/实施。也许将来可以通过lru_cache 为这些操作提供内置支持。

【讨论】:

  • 感谢代码,我试试,我认为这可能是一个很好的解决方案。我是功能请求的创建者;)看看日期。
  • 根据具体的用例,可能值得使用shelves 构建缓存,这基本上是持久性字典。
  • 这实际上是行不通的!您可以使用 pickle 将缓存保存到磁盘 - 但按说明加载它们不起作用。
  • @Nudin 你的意思是设置fibonacci.cache 不起作用吗?是的,应该是 fibonacci.__wrapped__.cache = ... 我稍微改变了装饰器,现在应该可以按预期工作了。
  • 这不是 LRU 缓存,它会无限增长。
【解决方案2】:

你可以使用我的库,mezmorize

import random
from mezmorize import Cache

cache = Cache(CACHE_TYPE='filesystem', CACHE_DIR='cache')


@cache.memoize()
def add(a, b):
    return a + b + random.randrange(0, 1000)

>>> add(2, 5)
727
>>> add(2, 5)
727

【讨论】:

  • 效果很好,但 CACHE_DIR 周围不应该有 '。不幸的是,只有至少 6 个字符才能进行编辑....
【解决方案3】:

考虑使用joblib.Memory 持久缓存到磁盘。

由于磁盘很大,因此不需要 LRU 缓存方案。

【讨论】:

    【解决方案4】:

    除了公共 API 之外,您不应该接触装饰器实现中的任何内容,因此如果您想更改其行为,您可能需要复制其实现并自己添加必要的函数。请注意,缓存当前存储为循环双向链表,因此您在保存和加载时需要小心。

    【讨论】:

    • 内部结构相当复杂。我可以编辑它们并公开缓存,但如果可能的话,我更喜欢不要更改默认库。
    • @FrancescoFrassinelli 我的意思是您可以将实现复制到您的 ptoject 中并进行更改。
    • 是的,我明白了。不存在导出函数(编组?)或仅导出和导入缓存(检查?)的方法。
    【解决方案5】:

    这是我写的东西,可能对devcache有帮助。

    它旨在帮助您加快长期运行方法的迭代速度。可以通过配置文件进行配置

    @devcache(group='crm')
    def my_method(a, b, c):  
        ...        
    
    @devcache(group='db')
    def another_method(a, b, c): 
        ...        
    

    缓存可以刷新或与 yaml 配置文件一起使用,例如:

    refresh: false # refresh true will ignore use_cache and refresh all cached data 
    props:
        1:
            group: crm
            use_cache: false
        2:
            group: db
            use_cache: true
    

    将刷新my_method 的缓存并使用another_method 的缓存。

    它不会帮助您腌制可调用对象,但它会完成缓存部分,并且可以直接修改代码以添加专门的序列化。

    【讨论】:

    • devcache 似乎是一个有趣的库,它使用 sqlite 作为缓存,具有额外的 conf,用于更复杂的大小写。如果是这样的话,依赖memcached或Redis不是更好吗?在您的 README.md 文件中进行这样的比较可能会很有趣。注意:如果您的数据库中的查询需要几分钟时间,则可能有问题,或者您可能正在使用物化视图进行评估。
    • 感谢您查看。 Redis 的优点是,将数据存储设置为可配置的,包括 memcached 或 Redis,这是我看到的方法。如果有需要,我会把它放在路线图上。 devcache 的主要附加值是能够选择性地使缓存无效并选择要包含在缓存键中的参数。完全同意从数据库中获取数据的时间。我正在处理的数据库有一个非常粗略的数据模型,它不在项目范围内对其进行返工或添加视图。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-19
    • 2017-04-24
    • 2014-05-09
    • 2020-10-14
    • 2015-08-08
    • 2012-09-23
    • 1970-01-01
    相关资源
    最近更新 更多