web-dev-qa-db-ja.com

Lru_cache(functoolsから)はどのように機能しますか?

特に再帰的なコードを使用する場合、lru_cache。キャッシュは、高速で提供する必要があるデータを保存し、コンピューターの再計算を回避するスペースであることを理解しています。

Pythonlru_cache functoolsから内部的に動作しますか?

特定の答えを探していますが、Pythonの他の部分のような辞書を使用していますか? return値のみを保存しますか?

Pythonは辞書の上に大きく構築されていることは知っていますが、この質問に対する具体的な答えは見つかりませんでした。うまくいけば、誰かがStackOverflowのすべてのユーザーに対してこの答えを簡略化できることを願っています。

19
Yono

Functoolsのソースはこちらから入手できます。 https://github.com/python/cpython/blob/3.6/Lib/functools.py

Lru_cacheデコレータには、コンテキスト内にcache辞書があります(すべての装飾された関数には独自のキャッシュ辞書があります)呼び出された関数の戻り値を保存します。辞書キーは、引数に従って_make_key関数で生成されます。いくつかの大胆なコメントを追加しました:

# one of decorator variants from source:
def _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo):
    sentinel = object()      # unique object used to signal cache misses

    cache = {}                                # RESULTS SAVES HERE
    cache_get = cache.get    # bound method to lookup a key or return None
    # ...

    def wrapper(*args, **kwds):
        # Simple caching without ordering or size limit
        nonlocal hits, misses
        key = make_key(args, kwds, typed)     # BUILD A KEY FROM ARGUMENTS
        result = cache_get(key, sentinel)     # TRYING TO GET PREVIOUS CALLS RESULT
        if result is not sentinel:            # ALREADY CALLED WITH PASSED ARGUMENTS
            hits += 1
            return result                     # RETURN SAVED RESULT
                                              # WITHOUT ACTUALLY CALLING FUNCTION
        result = user_function(*args, **kwds) # FUNCTION CALL - if cache[key] empty
        cache[key] = result                   # SAVE RESULT
        misses += 1
        return result
    # ...

    return wrapper
12
ndpu

ソースコードを確認できます here

基本的に、2つのデータ構造、dictionary関数パラメーターをその結果にマッピングする、およびlinked list関数呼び出し履歴を追跡します。

キャッシュは基本的に以下を使用して実装されますが、これは一目瞭然です。

cache = {}
cache_get = cache.get
....
make_key = _make_key         # build a key from the function arguments
key = make_key(args, kwds, typed)
result = cache_get(key, sentinel)

リンクされたリストを更新する要点は次のとおりです。

Elif full:

    oldroot = root
    oldroot[KEY] = key
    oldroot[RESULT] = result

    # update the linked list to pop out the least recent function call information        
    root = oldroot[NEXT]
    oldkey = root[KEY]
    oldresult = root[RESULT]
    root[KEY] = root[RESULT] = None
    ......