関数の出力をディスクにメモ化する方法はありますか?
機能があります
def getHtmlOfUrl(url):
... # expensive computation
そして、次のようなことをしたいと思います:
def getHtmlMemoized(url) = memoizeToFile(getHtmlOfUrl, "file.dat")
次に、getHtmlMemoized(url)を呼び出して、URLごとに1回だけ高価な計算を実行します。
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
それができたら、@-syntaxを使用して関数を「装飾」し、準備が整います。
@persist_to_file('cache.dat')
def html_of_url(url):
your function code...
このデコレータは意図的に簡略化されており、たとえば、ソース関数が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
チェックアウト - joblib.Memory
。それはまさにそれを行うためのライブラリです。
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
これにより、関数を1回だけ計算できるようになります。次の後続の呼び出しは、保存された結果を返します。
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
内部的には、入力引数からハッシュを作成し、このハッシュによってメモファイルを保存します。
diskcache
もあります。
from diskcache import Cache
cache = Cache("cachedir")
@cache.memoize()
def f(x, y):
print('Running f({}, {})'.format(x, y))
return x, y
データがjsonシリアル化可能であると仮定すると、このコードは機能するはずです
import os, json
def json_file(fname):
def decorator(function):
def wrapper(*args, **kwargs):
if os.path.isfile(fname):
with open(fname, 'r') as f:
ret = json.load(f)
else:
with open(fname, 'w') as f:
ret = function(*args, **kwargs)
json.dump(ret, f)
return ret
return wrapper
return decorator
getHtmlOfUrl
を装飾し、それを呼び出すだけです。以前に実行されていた場合は、キャッシュされたデータを取得します。
python 2.xおよびpython 3.x
Cache_to_diskパッケージを使用できます。
from cache_to_disk import cache_to_disk
@cache_to_disk(3)
def my_func(a, b, c, d=None):
results = ...
return results
これにより、引数a、b、c、およびdに固有の結果が3日間キャッシュされます。結果はマシンのピクルスファイルに保存され、次に関数が呼び出されたときにピクルスが解除されて返されます。 3日後、関数が再実行されるまで、pickleファイルは削除されます。関数が新しい引数で呼び出されるたびに、関数は再実行されます。詳細はこちら: https://github.com/sarenehan/cache_to_disk
このようなことをする必要があります:
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]
基本的な使い方:
your_mem_func = Memoize(your_func)
your_mem_func.load_memo('yourdata.json')
# do your stuff with your_mem_func
"cache"を使用後にファイルに書き込みたい場合は、後で再度ロードします。
your_mem_func.save_memo('yournewdata.json')