文字列を自然言語処理モジュールの一部であるTextBlobに変換する Python コマンド ライン ユーティリティを作成しています。モジュールのインポートは非常に遅く、私のシステムでは約 300 ミリ秒です。高速化のために、最初に関数が呼び出されたときにのみテキストを TextBlob に変換するメモ化関数を作成しました。重要なのは、同じテキストに対してスクリプトを 2 回実行する場合、TextBlob を再インポートして BLOB を再計算するのではなく、キャッシュから取得することを避けたいということです。これですべて完了し、正常に動作しますが、何らかの理由で関数がまだ非常に遅いことを除きます。実際、以前と同じくらい遅いです。これは、関数がメモ化されていて、メモ化された関数内で import ステートメントが発生しているにもかかわらず、モジュールが再インポートされているためだと思います。
ここでの目標は、次のコードを修正して、結果を再計算する必要がないことを前提として、メモ化された実行が必要なだけ高速になるようにすることです。
コア コードの最小限の例を次に示します。
@memoize
def make_blob(text):
import textblob
return textblob.TextBlob(text)
if __name__ == '__main__':
make_blob("hello")
そして、メモ化デコレータは次のとおりです。
import os
import shelve
import functools
import inspect
def memoize(f):
"""Cache results of computations on disk in a directory called 'cache'."""
path_of_this_file = os.path.dirname(os.path.realpath(__file__))
cache_dirname = os.path.join(path_of_this_file, "cache")
if not os.path.isdir(cache_dirname):
os.mkdir(cache_dirname)
cache_filename = f.__module__ + "." + f.__name__
cachepath = os.path.join(cache_dirname, cache_filename)
try:
cache = shelve.open(cachepath, protocol=2)
except:
print 'Could not open cache file %s, maybe name collision' % cachepath
cache = None
@functools.wraps(f)
def wrapped(*args, **kwargs):
argdict = {}
# handle instance methods
if hasattr(f, '__self__'):
args = args[1:]
tempargdict = inspect.getcallargs(f, *args, **kwargs)
for k, v in tempargdict.iteritems():
argdict[k] = v
key = str(hash(frozenset(argdict.items())))
try:
return cache[key]
except KeyError:
value = f(*args, **kwargs)
cache[key] = value
cache.sync()
return value
except TypeError:
call_to = f.__module__ + '.' + f.__name__
print ['Warning: could not disk cache call to ',
'%s; it probably has unhashable args'] % (call_to)
return f(*args, **kwargs)
return wrapped
そして、これはメモ化が現在時間を節約しないことのデモンストレーションです:
❯ time python test.py
python test.py 0.33s user 0.11s system 100% cpu 0.437 total
~/Desktop
❯ time python test.py
python test.py 0.33s user 0.11s system 100% cpu 0.436 total
これは、関数が正しくメモ化されている場合でも発生します (メモ化された関数内に配置された print ステートメントは、スクリプトが最初に実行されたときにのみ出力を提供します)。
役に立つ場合に備えて、すべてを GitHub Gistにまとめました。