以下を検討してください。
@property
def name(self):
if not hasattr(self, '_name'):
# expensive calculation
self._name = 1 + 1
return self._name
私は新しいですが、キャッシュはデコレータに組み入れることができると思います。私だけがそのようなものを見つけられませんでした;)
PS実際の計算は可変値に依存しません
以下を検討してください。
@property
def name(self):
if not hasattr(self, '_name'):
# expensive calculation
self._name = 1 + 1
return self._name
私は新しいですが、キャッシュはデコレータに組み入れることができると思います。私だけがそのようなものを見つけられませんでした;)
PS実際の計算は可変値に依存しません
回答:
Python 3.2から、組み込みのデコレータがあります:
@functools.lru_cache(maxsize=100, typed=False)
最新の呼び出しを最大サイズまで保存するメモ可能な呼び出し可能関数で関数をラップするデコレーター。高価な関数やI / Oバウンド関数が同じ引数で定期的に呼び出されると、時間を節約できます。
フィボナッチ数列を計算するためのLRUキャッシュの例:
@lru_cache(maxsize=None)
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
>>> print([fib(n) for n in range(16)])
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]
>>> print(fib.cache_info())
CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)
Python 2.xに悩まされている場合、互換性のある他のメモ化ライブラリのリストを次に示します。
lru_cache
には、キャッシュしている結果のコピーを作成する必要があり、functools.lru_cache
実装ではそのようなコピーは作成されません。これを行うと、ラージオブジェクトをキャッシュするために使用するときに、見つけにくいメモリの問題が発生するリスクがあります。
汎用のメモ化デコレータを要求していないように思えます(つまり、さまざまな引数値の戻り値をキャッシュする一般的なケースには興味がありません)。つまり、これが必要です。
x = obj.name # expensive
y = obj.name # cheap
一方、汎用のメモ化デコレータはこれを提供します:
x = obj.name() # expensive
y = obj.name() # cheap
プロパティの構文がクイックルックアップを提案する一方で、メソッド呼び出しの構文はより優れたスタイルであると私は考えています。
[更新:以前リンクしてここで引用したクラスベースのメモ化デコレーターは、メソッドでは機能しません。私はそれをデコレーター関数に置き換えました。]汎用のメモ化デコレーターを使用したい場合は、以下に簡単なものを示します。
def memoize(function):
memo = {}
def wrapper(*args):
if args in memo:
return memo[args]
else:
rv = function(*args)
memo[args] = rv
return rv
return wrapper
使用例:
@memoize
def fibonacci(n):
if n < 2: return n
return fibonacci(n - 1) + fibonacci(n - 2)
キャッシュサイズに制限のある別のメモ化デコレータは、ここにあります。
fibonacci
です。その関数は常に同じmemo
辞書を使用します。
class memorize(dict):
def __init__(self, func):
self.func = func
def __call__(self, *args):
return self[args]
def __missing__(self, key):
result = self[key] = self.func(*key)
return result
使用例:
>>> @memorize
... def foo(a, b):
... return a * b
>>> foo(2, 4)
8
>>> foo
{(2, 4): 8}
>>> foo('hi', 3)
'hihihi'
>>> foo
{(2, 4): 8, ('hi', 3): 'hihihi'}
Python 3.8 functools.cached_property
デコレーター
https://docs.python.org/dev/library/functools.html#functools.cached_property
cached_property
Werkzeugから:https ://stackoverflow.com/a/5295190/895245で言及されていましたが、おそらく派生バージョンが3.8にマージされます。これは素晴らしいことです。
このデコレーターは、キャッシング@property
、または@functools.lru_cache
引数がない場合のクリーナーとして 見ることができます。
ドキュメントは言う:
@functools.cached_property(func)
クラスのメソッドを、値が1回計算され、インスタンスの有効期間中は通常の属性としてキャッシュされるプロパティに変換します。property()に似ていますが、キャッシングが追加されています。他の方法では効果的に不変である、インスタンスの高価な計算プロパティに役立ちます。
例:
class DataSet: def __init__(self, sequence_of_numbers): self._data = sequence_of_numbers @cached_property def stdev(self): return statistics.stdev(self._data) @cached_property def variance(self): return statistics.variance(self._data)
バージョン3.8の新機能。
注このデコレーターでは、各インスタンスのdict属性が変更可能なマッピングである必要があります。これは、メタクラスなどの一部のタイプ(タイプインスタンスのdict属性はクラス名前空間の読み取り専用プロキシであるため)、および定義されたスロットの1つとしてdictを含めずにスロットを指定するタイプ(そのようなクラスなど)では機能しないことを意味しますdict属性をまったく提供しないでください)。
この単純なデコレータクラスをコーディングして、関数の応答をキャッシュしました。私のプロジェクトには非常に便利です。
from datetime import datetime, timedelta
class cached(object):
def __init__(self, *args, **kwargs):
self.cached_function_responses = {}
self.default_max_age = kwargs.get("default_cache_max_age", timedelta(seconds=0))
def __call__(self, func):
def inner(*args, **kwargs):
max_age = kwargs.get('max_age', self.default_max_age)
if not max_age or func not in self.cached_function_responses or (datetime.now() - self.cached_function_responses[func]['fetch_time'] > max_age):
if 'max_age' in kwargs: del kwargs['max_age']
res = func(*args, **kwargs)
self.cached_function_responses[func] = {'data': res, 'fetch_time': datetime.now()}
return self.cached_function_responses[func]['data']
return inner
使い方は簡単です:
import time
@cached
def myfunc(a):
print "in func"
return (a, datetime.now())
@cached(default_max_age = timedelta(seconds=6))
def cacheable_test(a):
print "in cacheable test: "
return (a, datetime.now())
print cacheable_test(1,max_age=timedelta(seconds=5))
print cacheable_test(2,max_age=timedelta(seconds=5))
time.sleep(7)
print cacheable_test(3,max_age=timedelta(seconds=5))
@cached
は括弧がありません。それ以外の場合はcached
、代わりにオブジェクトを返すだけmyfunc
で、呼び出されたmyfunc()
場合inner
は常に戻り値として返されます
免責事項:私はkids.cacheの作成者です。
確認する必要kids.cache
があります@cache
。Python2およびPython 3で動作するデコレータが提供されています。依存関係はありません。コードは100行以下です。たとえば、コードを念頭に置くと、次のように使用できます。
pip install kids.cache
その後
from kids.cache import cache
...
class MyClass(object):
...
@cache # <-- That's all you need to do
@property
def name(self):
return 1 + 1 # supposedly expensive calculation
または@cache
、@property
(同じ結果)の後にデコレータを置くこともできます。
財産上のキャッシュを使用すると呼ばれる、遅延評価、kids.cache
はるかに(それは...任意の引数、プロパティ、メソッドのいずれかのタイプ、さらにはクラスと機能上で動作)を行うことができます。上級ユーザー向けに、python 2およびpython 3(LRU、LFU、TTL、RRキャッシュ)に豪華なキャッシュストアを提供するkids.cache
サポートcachetools
。
重要な注意:のデフォルトのキャッシュストアkids.cache
は標準の辞書です。これは、異なるクエリで長時間実行されるプログラムには推奨されません。これは、キャッシュストアが増大し続けるためです。この使用法では、たとえば、(@cache(use=cachetools.LRUCache(maxsize=2))
関数/プロパティ/クラス/メソッドを装飾するために...)を使用して他のキャッシュストアをプラグインできます。
c
を作成し、MyClass
それを使用して検査しobjgraph.show_backrefs([c], max_depth=10)
ます。クラスオブジェクトMyClass
からへの参照チェーンがありますc
。つまり、c
リリースされるまでリリースされませんでしたMyClass
。
「Python 3 functools.lru_cacheのC実装。標準ライブラリの10〜30倍のスピードアップを提供します」というfastcacheがあります。
選択した回答と同じですが、インポートが異なります:
from fastcache import lru_cache
@lru_cache(maxsize=128, typed=False)
def f(a, b):
pass
また、インストールが必要な functoolsとは異なり、Anaconda にもインストールされます。
functools
は標準ライブラリの一部であり、投稿したリンクはランダムなgitフォークなどへのリンクです
Python Wikiには、メモ化デコレータのもう1つの例があります。
http://wiki.python.org/moin/PythonDecoratorLibrary#Memoize
この例は、パラメータが変更可能な場合に結果をキャッシュしないため、少し賢いです。(そのコードを確認してください。非常にシンプルで興味深いものです!)
Django Frameworkを使用している場合は、APIの使用のビューまたは応答をキャッシュするそのようなプロパティ@cache_page(time)
があり、他のオプションもある場合があります。
例:
@cache_page(60 * 15, cache="special_cache")
def my_view(request):
...
詳細については、こちらをご覧ください。
私はこのようなものを実装し、永続化にpickleを使用し、ほぼ確実に一意の短いIDにsha1を使用しました。基本的に、キャッシュは関数のコードと引数の履歴をハッシュしてsha1を取得し、名前にそのsha1が含まれるファイルを探しました。存在する場合は、それを開いて結果を返しました。そうでない場合は、関数を呼び出して結果を保存します(オプションで、処理に一定の時間がかかった場合のみ保存します)。
そうは言っても、これを実行する既存のモジュールを見つけて、ここでそのモジュールを見つけようとしていることを確信します... http://chase-seibert.github。 io / blog / 2011/11/23 / pythondjango-disk-based-caching-decorator.html
それに関して私が目にする唯一の問題は、巨大な配列に対して一意ではないstr(arg)をハッシュするため、大きな入力に対してはうまく機能しないことです。
クラスがその内容の安全なハッシュを返すようなunique_hash()プロトコルがあったらいいですね。基本的には、私が気にしたタイプに対して手動で実装しました。
joblib http://pythonhosted.org/joblib/memory.htmlを試してください
from joblib import Memory
memory = Memory(cachedir=cachedir, verbose=0)
@memory.cache
def f(x):
print('Running f(%s)' % x)
return x
Djangoを使用していてビューをキャッシュしたい場合は、Nikhil Kumarの回答を参照してください。
しかし、関数の結果をキャッシュしたい場合は、django-cache-utilsを使用できます。
Djangoキャッシュを再利用し、使いやすいcached
デコレーターを提供します。
from cache_utils.decorators import cached
@cached(60)
def foo(x, y=0):
print 'foo is called'
return x+y
@lru_cache
デフォルトの関数値では完全ではありません
私のmem
デコレータ:
import inspect
def get_default_args(f):
signature = inspect.signature(f)
return {
k: v.default
for k, v in signature.parameters.items()
if v.default is not inspect.Parameter.empty
}
def full_kwargs(f, kwargs):
res = dict(get_default_args(f))
res.update(kwargs)
return res
def mem(func):
cache = dict()
def wrapper(*args, **kwargs):
kwargs = full_kwargs(func, kwargs)
key = list(args)
key.extend(kwargs.values())
key = hash(tuple(key))
if key in cache:
return cache[key]
else:
res = func(*args, **kwargs)
cache[key] = res
return res
return wrapper
テスト用のコード:
from time import sleep
@mem
def count(a, *x, z=10):
sleep(2)
x = list(x)
x.append(z)
x.append(a)
return sum(x)
def main():
print(count(1,2,3,4,5))
print(count(1,2,3,4,5))
print(count(1,2,3,4,5, z=6))
print(count(1,2,3,4,5, z=6))
print(count(1))
print(count(1, z=10))
if __name__ == '__main__':
main()
結果-睡眠で3回のみ
しかし、@lru_cache
これは4回になります。これは、
print(count(1))
print(count(1, z=10))
2回計算されます(デフォルトでの作業は不適切)