缓存(保存/存储/记忆)的最佳数据结构是什么,这么多功能导致数据库。 假设函数calc_regress在python中具有流动定义:
def calc_regress(ind_key, dep_key, count=30):
independent_list = sql_select_recent_values(count, ind_key)
dependant_list = sql_select_recent_values(count, dep_key)
import scipy.stats as st
return st.linregress(independent_list, dependant_list)
我看到了What kind of table structure should be used to store memoized function parameters and results in a relational database?的答案,但它似乎解决了一个函数的问题,而我有大约500个函数。
答案 0 :(得分:1)
选项A
您可以使用链接答案中的结构,未使用列数标准化= 500个函数中的最大参数数。还需要为函数名添加一列。
然后你可以做SELECT * FROM expensive_func_results WHERE func_name = 'calc_regress' AND arg1 = ind_key AND arg2 = dep_key and arg3 = count
等等。
当然,这不是一个非常好的设计。对于使用较少参数调用的相同函数,需要忽略具有空值/不匹配的列;否则你会得到多个结果行。
选项B
将表/结构创建为func_name
,arguments
,result
其中'arguments'始终是kwargs字典或位置args但不是每个条目混合。即使将kwargs dict存储为字符串,其中的键 - >值的顺序也是不可预测/一致的,即使它是相同的args。因此,您需要在转换为字符串并存储之前对其进行排序。当您想要查询时,您将使用SELECT * FROM expensive_func_results WHERE func_name = 'calc_regress' AND arguments = 'str(kwargs_dict)'
,其中str(kwargs_dict)
是您将以编程方式设置的内容。它也可以设置为inspect.getargspec
,(或inspect.getcallargs
)的结果,但您必须检查一致性。
除非您提供查询的所有参数或与LIKE
部分匹配,否则您将无法对参数组合进行查询。
选项C
一直规范化:一个表func_calls
为func_name
,args_combo_id
,arg_name_idx
,arg_value
。该表的每一行将存储一个arg,用于该函数调用args的一个组合。另一个表格func_results
为func_name
,args_combo_id
,result
。您还可以进一步规范化func_name
以映射到func_id
。
在这一篇中,关键字args的顺序无关紧要,因为您将进行内部联接以选择每个参数。此查询必须以编程方式构建或通过存储过程完成,因为获取所有参数所需的连接数由参数数量决定。你上面的函数有3个参数,但你可能有10个参数。arg_name_idx
是'参数名称或索引'所以它也适用于混合kwargs + args。在calc_regress(ind_key=1, dep_key=2, count=30)
和calc_regress(1, 2, 30)
(以及calc_regress(1, 2)
的情况下可能会发生一些重复,其中包含count<的默认值 - 应该避免这种情况,表条目应该包含所有参数);因为args_combo_id
对于两者都不同,但结果显然是相同的。同样,检查模块可以在这方面提供帮助。
[编辑] PS:此外,对于func_name
,您可能需要使用完全限定名称以避免程序包中的模块之间发生冲突。装饰者也可能会干扰它;没有deco.__name__ = func.__name__
等等。
PPS:如果正在将对象传递给数据库中被记忆的函数,请确保它们的__str__
是有用的&可重复/一致存储为arg值。
这种特殊情况不要求你从数据库中的arg值重新创建对象,否则,你需要像__repr__
was intended to be那样__str__
或__repr__
(但通常不会这样做):
这应该看起来像一个有效的Python表达式,可用于重新创建具有相同值的对象(给定适当的环境)。
答案 1 :(得分:1)
我在这里使用密钥值存储,其中密钥可以是函数对象的id
的串联(以保证密钥单一性)及其参数,而值将是函数返回值。
因此,calc_regress(1, 5, 30)
调用会产生一个示例键139694472779248_1_5_30
,其中第一部分为id(calc_regress)
。密钥生成函数示例:
>>> def produce_cache_key(fun, *args, **kwargs):
... args_key = '_'.join(str(a) for a in args)
... kwargs_key = '_'.join('%s%s' % (k, v) for k, v in kwargs.items())
... return '%s_%s_%s' % (id(fun), args_key, kwargs_key)
您可以使用字典和装饰器将结果保存在内存中:
>>> def cache_result(cache):
... def decorator(fun):
... def wrapper(*args, **kwargs):
... key = produce_cache_key(fun, *args, **kwargs)
... if key not in cache:
... cache[key] = fun(*args, **kwargs)
... return cache[key]
... return wrapper
... return decorator
...
>>>
>>> @cache_result(cache_dict)
... def fx(x, y, z=0):
... print 'Doing some expensive job...'
...
>>> cache = {}
>>> fx(1, 2, z=1)
Doing some expensive job...
>>> fx(1, 2, z=1)
>>>