Tob*_*ias 133 python caching decorator
考虑以下:
@property
def name(self):
if not hasattr(self, '_name'):
# expensive calculation
self._name = 1 + 1
return self._name
Run Code Online (Sandbox Code Playgroud)
我是新手,但我认为缓存可能会被装入装饰器中.只有我找不到喜欢它的人;)
PS真正的计算不依赖于可变值
Pao*_*tti 178
从Python 3.2开始,有一个内置的装饰器:
@functools.lru_cache(maxsize=100, typed=False)
Decorator用一个memoizing callable来包装一个函数,它可以节省maxsize最近的调用.当使用相同的参数定期调用昂贵的或I/O绑定函数时,它可以节省时间.
用于计算Fibonacci数的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)
Run Code Online (Sandbox Code Playgroud)
如果你坚持使用Python 2.x,这里有一个其他兼容的memoization库列表:
functools32| PyPI | 源代码repoze.lru| PyPI | 源代码pylru| PyPI | 源代码backports.functools_lru_cache| PyPI | 源代码Nat*_*hen 27
听起来你并不是要求一个通用的memoization装饰器(也就是说,你不想对你想要为不同的参数值缓存返回值的一般情况).也就是说,你想拥有这个:
x = obj.name # expensive
y = obj.name # cheap
Run Code Online (Sandbox Code Playgroud)
而通用的memoization装饰器会给你这个:
x = obj.name() # expensive
y = obj.name() # cheap
Run Code Online (Sandbox Code Playgroud)
我认为方法调用语法是更好的样式,因为它提示了昂贵计算的可能性,而属性语法建议快速查找.
[更新:我之前链接并引用的基于类的memoization装饰器不适用于方法.我用装饰器函数替换它.]如果你愿意使用通用的memoization装饰器,这里有一个简单的:
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
Run Code Online (Sandbox Code Playgroud)
用法示例:
@memoize
def fibonacci(n):
if n < 2: return n
return fibonacci(n - 1) + fibonacci(n - 2)
Run Code Online (Sandbox Code Playgroud)
可以在此处找到另一个具有高速缓存大小限制的memoization装饰器.
acm*_*ght 22
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
Run Code Online (Sandbox Code Playgroud)
样品用途:
>>> @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'}
Run Code Online (Sandbox Code Playgroud)
小智 9
我编写了这个简单的装饰器类来缓存函数响应.我发现它对我的项目非常有用:
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
Run Code Online (Sandbox Code Playgroud)
用法很简单:
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))
Run Code Online (Sandbox Code Playgroud)
尝试 joblib https://joblib.readthedocs.io/en/latest/memory.html
from joblib import Memory
# customize the decorator
memory = Memory(cachedir=cachedir, verbose=0)
@memory.cache
def f(x):
print('Running f(%s)' % x)
return x
Run Code Online (Sandbox Code Playgroud)
免责声明:我是kids.cache的作者.
你应该检查一下kids.cache,它提供了一个@cache适用于python 2和python 3 的装饰器.没有依赖关系,~100行代码.例如,考虑到您的代码,使用起来非常简单,您可以像这样使用它:
pip install kids.cache
Run Code Online (Sandbox Code Playgroud)
然后
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
Run Code Online (Sandbox Code Playgroud)
或者你可以把@cache装饰器放在@property(相同的结果)之后.
在属性上使用缓存称为延迟评估,kids.cache可以做更多的事情(它适用于任何参数,属性,任何类型的方法甚至类的函数...).对于高级用户,kids.cache支持cachetools为python 2和python 3(LRU,LFU,TTL,RR缓存)提供精美的缓存存储.
重要说明:默认缓存存储kids.cache是一个标准的dict,不推荐用于长期运行的程序,因为它会导致不断增长的缓存存储.对于此用法,您可以使用例如插入其他缓存存储(@cache(use=cachetools.LRUCache(maxsize=2))以装饰您的函数/属性/类/方法...)
Python 3.8 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。
文档说:
Run Code Online (Sandbox Code Playgroud)@functools.cached_property(func)将类的方法转换为属性,该属性的值将被计算一次,然后在实例生命周期中作为常规属性进行缓存。与property()类似,但增加了缓存。对于实例有效的不可变的昂贵的计算属性很有用。
例:
Run Code Online (Sandbox Code Playgroud)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属性是类名称空间的只读代理),以及那些指定槽位但不将dict用作已定义槽位之一的类(例如此类)根本不提供dict属性)。
functools.cache已在 Python 3.9 (文档) 中发布:
from functools import cache
@cache
def factorial(n):
return n * factorial(n-1) if n else 1
Run Code Online (Sandbox Code Playgroud)
在以前的 Python 版本中,早期的答案之一仍然是一个有效的解决方案,将其lru_cache用作没有限制和 lru 功能的普通缓存。(文档)
如果 maxsize 设置为 None,则禁用 LRU 功能,缓存可以无限制地增长。
这是它的一个更漂亮的版本:
cache = lru_cache(maxsize=None)
@cache
def func(param1):
pass
Run Code Online (Sandbox Code Playgroud)
Python Wiki 上还有另一个memoize装饰器的示例:
http://wiki.python.org/moin/PythonDecoratorLibrary#Memoize
这个例子有点聪明,因为如果参数是可变的,它不会缓存结果。(检查该代码,它非常简单且有趣!)