有一个装饰器来简单地caching函数返回值吗?
考虑以下几点:
@property def name(self): if not hasattr(self, '_name'): # expensive calculation self._name = 1 + 1 return self._name
我是新的,但我认为caching可以被分解成一个装饰器。 只有我没有find像这样的人;)
PS真正的计算不依赖于可变值
从Python 3.2开始,有一个内置的装饰器:
@functools.lru_cache(maxsize=100, typed=False)
装饰者用一个可调用的可调用函数来包装一个函数,以保存最近最大的调用。 当使用相同的参数定期调用昂贵的或I / O绑定函数时,可以节省时间。
用于计算斐波那契数的LRUcaching示例:
@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,下面列出了其他兼容的memoization库:
-
functools32
| PyPI | 源代码 -
repoze.lru
| PyPI | 源代码 -
pylru
| PyPI | 源代码 -
backports.functools_lru_cache
| PyPI | 源代码
这听起来像你不是要求一个通用的memoization装饰器(即,你不想要caching返回值为不同的参数值的一般情况下)。 也就是说,你想要这样做:
x = obj.name # expensive y = obj.name # cheap
而一个通用的memoization装饰会给你这个:
x = obj.name() # expensive y = obj.name() # cheap
我认为方法调用语法是更好的风格,因为它提示了可能的昂贵的计算,而属性语法提示快速查找。
[更新:我曾经链接到和以前引用的基于类的memoization装饰器不适用于方法。 我用一个装饰器函数replace了它。]如果你愿意使用通用的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
用法示例:
@memoize def fibonacci(n): if n < 2: return n return fibonacci(n - 1) + fibonacci(n - 2)
在这里可以find另一个对高速caching大小有限制的memoization装饰器。
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'}
Werkzeug有一个cached_property
装饰器( docs , source )
我编写了这个简单的装饰类来caching函数响应。 我觉得这对我的项目非常有用:
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))
啊,只需要find这个正确的名称:“ 懒惰的财产评估 ”。
我也是这么做的 也许我会在我的代码中使用该配方。
免责声明:我是kids.cache的作者。
你应该检查kids.cache
,它提供了一个@cache
装饰器,它可以在python 2和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
装饰器放在@cache
之后(相同的结果)。
在一个属性上使用caching被称为懒惰评估 , kids.cache
可以做更多的事情(它可以在任何参数,属性,任何types的方法,甚至类上的function)。 对于高级用户, kids.cache
支持cachetools
,它为python 2和python 3(LRU,LFU,TTL,RRcaching)提供了奇特的caching存储。
Python Wiki中还有一个memoize装饰器的例子:
http://wiki.python.org/moin/PythonDecoratorLibrary#Memoize
这个例子有点聪明,因为如果参数是可变的,它不会caching结果。 (检查代码,这是非常简单而有趣的!)
如果你使用的是Django Framework,它有这样一个属性来cachingAPI的视图或响应,使用@cache_page(time),也可以有其他选项。
@cache_page(60 * 15, cache="special_cache") def my_view(request): ...
更多细节可以在这里find。
随着Memoize例子,我发现了下面的python包:
- caching 它允许设置ttl和/或caching函数的调用次数; 此外,可以使用encryption的基于文件的caching…
- percache
我实现了这样的东西,使用pickle来保存,并使用sha1作为short几乎肯定唯一的ID。 基本上,高速caching对函数的代码和参数的hist进行散列,以得到一个sha1,然后在这个名字中寻找一个带有sha1的文件。 如果存在,则打开并返回结果; 如果不是,则调用该函数并保存结果(如果需要一定的时间进行处理,则可select保存)。
这就是说,我发誓,我发现了一个现有的模块,这样做,发现自己在这里试图find该模块…最接近我能find的是,这看起来是正确的: http://chase-seibert.github。 IO /博客/ 2011/11/23 / -基于pythondjango磁盘caching,decorator.html
我看到的唯一的问题是它不适用于大型input,因为它的哈希str(arg),这不是巨型数组所特有的。
如果有一个具有类的unique_hash ()协议返回其内容的安全散列,那将会很好。 我基本上手动实现了我关心的types。