4

@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サポートしていますが、キャッシュは更新しません.call

使用に関するフォローアップklepto:

デフォルトで結果を特定の場所にキャッシュする方法はありますかklepto(@Wally の回答を参照)。(例/some/path/) この場所を複数の機能で共有しますか? 例えば、私は言いたい

cache_path = "/some/path/"

次に@memoize、同じパスの下の特定のモジュール内のいくつかの関数。

4

4 に答える 4

4

I would suggest looking at joblib and klepto. Both have very configurable caching algorithms, and may do what you want.

Both definitely can do the caching for result1 and result2, and klepto provides access to the cache, so one can pop a result from the local memory cache (without removing it from a stored archive, say in a database).

>>> 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

Not exactly the keyword interface you were looking for, but it has the functionality you are looking for.

于 2015-02-04T00:16:42.937 に答える
2

次のようなことができます。

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]
于 2015-02-04T00:25:27.107 に答える
1

これは純粋にklepto… のフォローアップの質問に関するものです。</p>

フローは、@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') 
于 2015-02-04T02:55:18.350 に答える
1

自分でやりたい場合:

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
于 2015-02-04T00:28:03.440 に答える