考虑以下几点:
@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,真正的计算不依赖于可变值
当前回答
听起来好像您不是在要求一个通用的记忆化装饰器(也就是说,您对想要缓存不同参数值的返回值的一般情况不感兴趣)。也就是说,你想要这样:
x = obj.name # expensive
y = obj.name # cheap
而一个通用的记忆装饰器会给你这样的:
x = obj.name() # expensive
y = obj.name() # cheap
我认为方法调用语法是更好的风格,因为它暗示了昂贵计算的可能性,而属性语法暗示了快速查找。
[更新:我之前链接并引用的基于类的记忆化装饰器不适用于方法。我用decorator函数替换了它。如果你愿意使用通用的记忆装饰器,这里有一个简单的:
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)
可以在这里找到另一个对缓存大小有限制的内存装饰器。
其他回答
创建自己的装饰器并使用它
from django.core.cache import cache
import functools
def cache_returned_values(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
key = "choose a unique key here"
results = cache.get(key)
if not results:
results = func(*args, **kwargs)
cache.set(key, results)
return results
return wrapper
现在看函数
@cache_returned_values
def get_some_values(args):
return x
我编写了这个简单的装饰器类来缓存函数响应。我发现它对我的项目非常有用:
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))
听起来好像您不是在要求一个通用的记忆化装饰器(也就是说,您对想要缓存不同参数值的返回值的一般情况不感兴趣)。也就是说,你想要这样:
x = obj.name # expensive
y = obj.name # cheap
而一个通用的记忆装饰器会给你这样的:
x = obj.name() # expensive
y = obj.name() # cheap
我认为方法调用语法是更好的风格,因为它暗示了昂贵计算的可能性,而属性语法暗示了快速查找。
[更新:我之前链接并引用的基于类的记忆化装饰器不适用于方法。我用decorator函数替换了它。如果你愿意使用通用的记忆装饰器,这里有一个简单的:
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)
可以在这里找到另一个对缓存大小有限制的内存装饰器。
我实现了类似的东西,使用pickle进行持久化,使用sha1进行简短的几乎唯一的id。基本上,缓存对函数代码和参数的历史进行哈希,以获得sha1,然后查找名称为sha1的文件。如果它存在,则打开它并返回结果;如果没有,则调用该函数并保存结果(如果需要一定时间来处理,则可以选择只保存结果)。
也就是说,我发誓我找到了一个现有的模块,它做到了这一点,并发现自己在这里试图找到该模块……我能找到的最接近的是这个,看起来差不多:http://chase-seibert.github.io/blog/2011/11/23/pythondjango-disk-based-caching-decorator.html
我看到的唯一问题是,它不能很好地用于大输入,因为它散列str(arg),这不是唯一的大型数组。
如果有一个unique_hash()协议,让一个类返回其内容的安全散列,那就太好了。我基本上是手动实现我所关心的类型。
Python 3.8 functools。cached_property装饰
https://docs.python.org/dev/library/functools.html#functools.cached_property
来自Werkzeug的cached_property在:https://stackoverflow.com/a/5295190/895245上提到过,但据说派生版本将合并到3.8中,这是非常棒的。
这个装饰器可以被看作是缓存@property,或者是清洁器@functools。Lru_cache,当你没有任何参数时。
医生说:
@functools.cached_property(func) Transform a method of a class into a property whose value is computed once and then cached as a normal attribute for the life of the instance. Similar to property(), with the addition of caching. Useful for expensive computed properties of instances that are otherwise effectively immutable. Example: 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) New in version 3.8. Note This decorator requires that the dict attribute on each instance be a mutable mapping. This means it will not work with some types, such as metaclasses (since the dict attributes on type instances are read-only proxies for the class namespace), and those that specify slots without including dict as one of the defined slots (as such classes don’t provide a dict attribute at all).