1

文字列を自然言語処理モジュールの一部である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にまとめました。

4

1 に答える 1

0

別のアプローチはどうですか:

import pickle

CACHE_FILE = 'cache.pkl'

try:
    with open(CACHE_FILE) as pkl:
        obj = pickle.load(pkl)
except:
    import slowmodule
    obj = "something"
    with open(CACHE_FILE, 'w') as pkl:
        pickle.dump(obj, pkl)

print obj

ここでは、モジュールではなくobjectをキャッシュします。オブジェクトがキャッシュに必要な場合、これは節約にはならないことに注意してくださいslowmodule。したがって、上記の例では、 は文字列であり、モジュールがそれを理解する"something"必要がないため、節約が見られます。slowmoduleしかし、あなたが次のようなことをした場合

obj = slowmodule.Foo("bar")

unpickle プロセスは、slowmodule を自動的にインポートし、キャッシュの利点を無効にします。

したがってtextblob.TextBlob(text)、 unpickled が textblob モジュールを必要としないものに操作できる場合は、このアプローチを使用して節約できます。

于 2015-03-06T08:34:18.063 に答える