memoize to disk - python - persistent memoization

前端 未结 9 844
再見小時候
再見小時候 2020-12-24 05:34

Is there a way to memoize the output of a function to disk?

I have a function

def getHtmlOfUrl(url):
    ... # expensive computation
<
相关标签:
9条回答
  • 2020-12-24 06:05

    Something like this should do:

    import json
    
    class Memoize(object):
        def __init__(self, func):
            self.func = func
            self.memo = {}
    
        def load_memo(filename):
            with open(filename) as f:
                self.memo.update(json.load(f))
    
        def save_memo(filename):
            with open(filename, 'w') as f:
                json.dump(self.memo, f)
    
        def __call__(self, *args):
            if not args in self.memo:
                self.memo[args] = self.func(*args)
            return self.memo[args]
    

    Basic usage:

    your_mem_func = Memoize(your_func)
    your_mem_func.load_memo('yourdata.json')
    #  do your stuff with your_mem_func
    

    If you want to write your "cache" to a file after using it -- to be loaded again in the future:

    your_mem_func.save_memo('yournewdata.json')
    
    0 讨论(0)
  • 2020-12-24 06:16

    Python offers a very elegant way to do this - decorators. Basically, a decorator is a function that wraps another function to provide additional functionality without changing the function source code. Your decorator can be written like this:

    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
    

    Once you've got that, 'decorate' the function using @-syntax and you're ready.

    @persist_to_file('cache.dat')
    def html_of_url(url):
        your function code...
    

    Note that this decorator is intentionally simplified and may not work for every situation, for example, when the source function accepts or returns data that cannot be json-serialized.

    More on decorators: How to make a chain of function decorators?

    And here's how to make the decorator save the cache just once, at exit time:

    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
    
    0 讨论(0)
  • 2020-12-24 06:17

    You can use the cache_to_disk package:

        from cache_to_disk import cache_to_disk
    
        @cache_to_disk(3)
        def my_func(a, b, c, d=None):
            results = ...
            return results
    

    This will cache the results for 3 days, specific to the arguments a, b, c and d. The results are stored in a pickle file on your machine, and unpickled and returned next time the function is called. After 3 days, the pickle file is deleted until the function is re-run. The function will be re-run whenever the function is called with new arguments. More info here: https://github.com/sarenehan/cache_to_disk

    0 讨论(0)
  • 2020-12-24 06:18

    A cleaner solution powered by Python's Shelve module. The advantage is the cache gets updated in real time via well-known dict syntax, also it's exception proof(no need to handle annoying 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
    

    This will facilitate the function to be computed just once. Next subsequent calls will return the stored result.

    0 讨论(0)
  • 2020-12-24 06:18

    The Artemis library has a module for this. (you'll need to pip install artemis-ml)

    You decorate your function:

    from artemis.fileman.disk_memoize import memoize_to_disk
    
    @memoize_to_disk
    def fcn(a, b, c = None):
        results = ...
        return results
    

    Internally, it makes a hash out of input arguments and saves memo-files by this hash.

    0 讨论(0)
  • 2020-12-24 06:19

    There is also diskcache.

    from diskcache import Cache
    
    cache = Cache("cachedir")
    
    @cache.memoize()
    def f(x, y):
        print('Running f({}, {})'.format(x, y))
        return x, y
    
    0 讨论(0)
提交回复
热议问题