memoize to disk - python - 持久性memoization

seg*_*uso 26 python memoization

有没有办法将函数的输出记忆到磁盘?

我有一个功能

def getHtmlOfUrl(url):
    ... # expensive computation
Run Code Online (Sandbox Code Playgroud)

并希望做类似的事情:

def getHtmlMemoized(url) = memoizeToFile(getHtmlOfUrl, "file.dat")
Run Code Online (Sandbox Code Playgroud)

然后调用getHtmlMemoized(url),以便为每个url只执行一次昂贵的计算.

geo*_*org 27

Python提供了一种非常优雅的方法 - 装饰器.基本上,装饰器是一个函数,它包含另一个函数以提供附加功能而无需更改函数源代码.你的装饰者可以像这样写:

import json

def persist_to_file(file_name):

    def decorator(original_func):

        try:
            cache = json.load(open(file_name, 'r'))
        except (IOError, ValueError):
            cache = {}

        def new_func(param):
            if param not in cache:
                cache[param] = original_func(param)
                json.dump(cache, open(file_name, 'w'))
            return cache[param]

        return new_func

    return decorator
Run Code Online (Sandbox Code Playgroud)

一旦你有了这个,使用@ -syntax"装饰"这个功能就可以了.

@persist_to_file('cache.dat')
def html_of_url(url):
    your function code...
Run Code Online (Sandbox Code Playgroud)

请注意,此装饰器是有意简化的,可能不适用于所有情况,例如,当源函数接受或返回无法json序列化的数据时.

有关装饰器的更多信息:如何制作一系列函数装饰器?

以下是如何使装饰器在退出时只保存一次缓存:

import json, atexit

def persist_to_file(file_name):

    try:
        cache = json.load(open(file_name, 'r'))
    except (IOError, ValueError):
        cache = {}

    atexit.register(lambda: json.dump(cache, open(file_name, 'w')))

    def decorator(func):
        def new_func(param):
            if param not in cache:
                cache[param] = func(param)
            return cache[param]
        return new_func

    return decorator
Run Code Online (Sandbox Code Playgroud)

  • 这将在每次更新缓存时写入一个新文件 - 取决于用例,这可能(或可能不会)击败你从memoization获得的加速.... (6认同)
  • 它***包含一个非常好的竞争条件,如果这个装饰器同时使用,或者(更可能)以可重入的方式使用.如果`a()`和`b()`都被记忆,而`a()`调用`b()`,则可以为`a()`读取缓存,然后再为`b()`读取缓存. ,第一个b的结果是memoized,但随后从调用的过时缓存覆盖它,b对缓存的贡献丢失. (3认同)

Wil*_*ill 20

退房joblib.Memory.这是一个完全正确的库.

  • 哇,这是一个多么棒的图书馆!我无法相信这些年来我没有工作经历.这应该是IMO的正确答案. (3认同)
  • 优点:对我有用,而且速度超级快。缺点:文件夹名称是字母和数字,例如“3e2bbf56be556090e768d65967cb9122”,输出是“.pkl”文件,因此我无法轻松查看缓存数据的样子。 (2认同)

C. *_*oli 12

还有diskcache

from diskcache import Cache

cache = Cache("cachedir")

@cache.memoize()
def f(x, y):
    print('Running f({}, {})'.format(x, y))
    return x, y
Run Code Online (Sandbox Code Playgroud)

  • 如果要记忆的函数是对象的方法,并且在该对象中初始化了“cache”变量,那么如何使用它? (3认同)

Pet*_*ter 6

Artemis 库有一个用于此目的的模块。(你需要pip install artemis-ml

你装饰你的功能:

from artemis.fileman.disk_memoize import memoize_to_disk

@memoize_to_disk
def fcn(a, b, c = None):
    results = ...
    return results
Run Code Online (Sandbox Code Playgroud)

在内部,它根据输入参数生成哈希值,并通过该哈希值保存备忘录文件。


the*_*oid 6

看看卡奇尔。它支持额外的缓存配置参数,如 TTL 等。

简单的例子:

from cachier import cachier
import datetime

@cachier(stale_after=datetime.timedelta(days=3))
def foo(arg1, arg2):
  """foo now has a persistent cache, trigerring recalculation for values stored more than 3 days."""
  return {'arg1': arg1, 'arg2': arg2}
Run Code Online (Sandbox Code Playgroud)


neh*_*iah 5

由Python的Shelve模块提供支持的更清洁的解决方案。优点是缓存可以通过众所周知的dict语法进行实时更新,这也是异常证明(无需处理烦人KeyError)。

import shelve
def shelve_it(file_name):
    d = shelve.open(file_name)

    def decorator(func):
        def new_func(param):
            if param not in d:
                d[param] = func(param)
            return d[param]

        return new_func

    return decorator

@shelve_it('cache.shelve')
def expensive_funcion(param):
    pass
Run Code Online (Sandbox Code Playgroud)

这将使函数仅被计算一次。下一个后续调用将返回存储的结果。