python - 如何根据返回的值缓存函数的结果
问题描述
我知道functools.lru_cache
and functools.cache
(从 Python 3.9 开始),但我很难缓存不返回None
(或任何其他特定值)的函数的这些参数:
from functools import lru_cache
@lru_cache
def my_fun(link):
res = fetch_data(link)
return res
res
fetch_data
遇到间歇性错误时为 None 。这是我不希望结果被缓存的时候。
解决方案
缓存具有位置参数的函数
我想我可以使用字典自己实现缓存并仅在返回值不是时存储结果None
:
from functools import wraps
def my_cache(no_cache_result=tuple()):
if no_cache_result is None:
no_cache_result = tuple()
cache = dict()
def decorator(fun):
@wraps(fun)
def wrapper(*args, **kwargs):
assert len(kwargs) == 0
if args in cache:
print('cache: taken from cache')
return cache[args]
else:
res = fun(*args, **kwargs)
if res not in no_cache_result:
print('cache: stored in cache')
cache[args] = res
else:
print('cache: NOT stored')
return res
return wrapper
return decorator
@my_cache(no_cache_result=[None])
def my_fun(a):
print(f'my_fun: called with {a}')
if a <= 1:
return a
else:
return None
my_fun(0)
my_fun(1)
my_fun(2)
my_fun(0)
my_fun(1)
my_fun(2)
哪个打印(如预期):
my_fun: called with 0
cache: stored in cache
my_fun: called with 1
cache: stored in cache
my_fun: called with 2
cache: NOT stored
cache: taken from cache
cache: taken from cache
my_fun: called with 2
cache: NOT stored
缓存具有位置和关键字参数的函数
上面的解决方案将可以修饰的函数限制为只有位置参数而不是关键字参数的函数。
以较小的减速为代价,可以通过以下方式进行改进:
def my_cache(no_cache_result=tuple()):
if no_cache_result is None:
no_cache_result = tuple()
cache = dict()
def decorator(fun):
@wraps(fun)
def wrapper(*args, **kwargs):
_kwargs = tuple(kwargs.items())
if (args, _kwargs) in cache:
print('cache: taken from cache')
return cache[(args, _kwargs)]
else:
res = fun(*args, **kwargs)
if res not in no_cache_result:
print('cache: stored in cache')
cache[(args, _kwargs)] = res
else:
print('cache: NOT stored')
return res
return wrapper
return decorator
哪个按预期工作:
@my_cache(no_cache_result=[None, ])
def my_fun2(a, b=7):
print(f'my_fun2: called with {a}, {b}')
if a <= 1:
return a
else:
return None
my_fun2(0, b=2)
my_fun2(1)
my_fun2(2)
my_fun2(0, b=2)
my_fun2(1)
my_fun2(2)
印刷:
my_fun2: called with 0, 2
cache: stored in cache
my_fun2: called with 1, 7
cache: stored in cache
my_fun2: called with 2, 7
cache: NOT stored
cache: taken from cache
cache: taken from cache
my_fun2: called with 2, 7
cache: NOT stored
这个怎么运作?
装饰器
您可以在对带参数的装饰器的回答中找到充分讨论的包装器(也可以带参数)的实现细节?.
禁止返回值 - 性能
缓存的性能取决于传递no_cache_result
参数的类型。如果您希望限制对多个返回值的缓存,建议传递一个集合,而不是通常使用的列表,因为if x in no_cache_result
集合的操作比列表快得多。
推荐阅读
- laravel - 如何解决条纹错误没有这样的计划:'每月'laravel收银员
- apache-spark - 为什么我的随机分区在分组操作期间不是 200(默认)?(火花 2.4.5)
- tfs - 在 TFS 上创建具有回溯/历史日期的任务
- angular - 通过单击 mat-select 中的复选框,不显示内容
- php - 如何将数据库传递给类?
- python - 获取文件中提及特定关键字的行
- c++ - 从 xs:dateTime 到 std::chrono::timepoint
- sql - 如何修复错误 变量名已被声明。变量名称在查询批处理或存储过程中必须是唯一的吗?
- google-chrome - iOS Safari WebRTC > Chrome > 其他浏览器黑屏
- android - 如何根据用户系统语言翻译资产中的html文件,如android studio中的Strings.xml