有一个装饰器来简单地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。