db中memoization的数据结构
Data structure of memoization in db
最好的数据结构是什么来缓存 (save/store/memorize) 如此多的函数结果在数据库中。
假设函数 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 个函数。
选项 A
您可以使用链接答案中的结构,未使用列数 = 500 个函数中参数的最大数量进行标准化。还需要为函数名称添加一列。
然后你可以做一个SELECT * FROM expensive_func_results WHERE func_name = 'calc_regress' AND arg1 = ind_key AND arg2 = dep_key and arg3 = count
,等等
当然,这不是一个很好用的设计。对于使用较少参数调用的相同函数,需要忽略具有空 values/non-matches 的列;否则你会得到多个结果行。
选项 B
将 table/structure 创建为 func_name
、arguments
、result
,其中 'arguments' 始终是 kwargs 字典或位置参数,但不是每个条目混合。即使将 kwargs dict 存储为字符串,其中键->值的顺序也不是 predictable/consistent 即使它是相同的参数。因此,您需要在转换为字符串并存储之前对其进行排序。当您想要查询时,您将使用 SELECT * FROM expensive_func_results WHERE func_name = 'calc_regress' AND arguments = 'str(kwargs_dict)'
,其中 str(kwargs_dict)
是您将以编程方式设置的内容。它也可以设置为 inspect.getargspec
, (or inspect.getcallargs
) 的结果,但您必须检查一致性。
您将无法对参数组合进行查询,除非您将所有参数提供给查询或与 LIKE
部分匹配。
选项 C
一直归一化:一个 table func_calls
为 func_name
、args_combo_id
、arg_name_idx
、arg_value
。 table 的每一行将为该函数的调用参数的一个组合存储一个参数。另一个tablefunc_results
为func_name
、args_combo_id
、result
。您还可以进一步规范化 func_name
映射到 func_id
.
在这一个中,关键字参数的顺序无关紧要,因为您将对 select 每个参数进行内部联接。此查询必须以编程方式构建或通过存储过程完成,因为获取所有参数所需的连接数由参数数决定。你上面的函数有 3 个参数,但你可能有另一个有 10 个参数。arg_name_idx
是 'argument name or index' 所以它也适用于混合 kwargs + args。在 calc_regress(ind_key=1, dep_key=2, count=30)
和 calc_regress(1, 2, 30)
(以及 calc_regress(1, 2)
具有默认计数值的情况下可能会出现一些重复 <-- 应避免这种情况,table 条目应有所有参数);因为 args_combo_id
两者都不同,但结果显然是相同的。同样,检查模块可能在这方面有所帮助。
[编辑] PS:此外,对于 func_name
,您可能需要使用完全限定名称以避免模块间冲突包裹。装饰器也可能会干扰它;没有 deco.__name__ = func.__name__
,等等
PPS:如果对象被传递给在数据库中存储的函数,请确保它们的 __str__
有用并且 repeatable/consistent 可以存储为 arg 值。
这种特殊情况不需要您从数据库中的 arg 值重新创建对象,否则,您需要像 __repr__
was intended to be(但通常不这样做):
this should look like a valid Python expression that could be used to recreate an object with the same value (given an appropriate environment).
我会在这里使用键值存储,其中键可以是函数对象的 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)
>>>
最好的数据结构是什么来缓存 (save/store/memorize) 如此多的函数结果在数据库中。 假设函数 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 个函数。
选项 A
您可以使用链接答案中的结构,未使用列数 = 500 个函数中参数的最大数量进行标准化。还需要为函数名称添加一列。
然后你可以做一个SELECT * FROM expensive_func_results WHERE func_name = 'calc_regress' AND arg1 = ind_key AND arg2 = dep_key and arg3 = count
,等等
当然,这不是一个很好用的设计。对于使用较少参数调用的相同函数,需要忽略具有空 values/non-matches 的列;否则你会得到多个结果行。
选项 B
将 table/structure 创建为 func_name
、arguments
、result
,其中 'arguments' 始终是 kwargs 字典或位置参数,但不是每个条目混合。即使将 kwargs dict 存储为字符串,其中键->值的顺序也不是 predictable/consistent 即使它是相同的参数。因此,您需要在转换为字符串并存储之前对其进行排序。当您想要查询时,您将使用 SELECT * FROM expensive_func_results WHERE func_name = 'calc_regress' AND arguments = 'str(kwargs_dict)'
,其中 str(kwargs_dict)
是您将以编程方式设置的内容。它也可以设置为 inspect.getargspec
, (or inspect.getcallargs
) 的结果,但您必须检查一致性。
您将无法对参数组合进行查询,除非您将所有参数提供给查询或与 LIKE
部分匹配。
选项 C
一直归一化:一个 table func_calls
为 func_name
、args_combo_id
、arg_name_idx
、arg_value
。 table 的每一行将为该函数的调用参数的一个组合存储一个参数。另一个tablefunc_results
为func_name
、args_combo_id
、result
。您还可以进一步规范化 func_name
映射到 func_id
.
在这一个中,关键字参数的顺序无关紧要,因为您将对 select 每个参数进行内部联接。此查询必须以编程方式构建或通过存储过程完成,因为获取所有参数所需的连接数由参数数决定。你上面的函数有 3 个参数,但你可能有另一个有 10 个参数。arg_name_idx
是 'argument name or index' 所以它也适用于混合 kwargs + args。在 calc_regress(ind_key=1, dep_key=2, count=30)
和 calc_regress(1, 2, 30)
(以及 calc_regress(1, 2)
具有默认计数值的情况下可能会出现一些重复 <-- 应避免这种情况,table 条目应有所有参数);因为 args_combo_id
两者都不同,但结果显然是相同的。同样,检查模块可能在这方面有所帮助。
[编辑] PS:此外,对于 func_name
,您可能需要使用完全限定名称以避免模块间冲突包裹。装饰器也可能会干扰它;没有 deco.__name__ = func.__name__
,等等
PPS:如果对象被传递给在数据库中存储的函数,请确保它们的 __str__
有用并且 repeatable/consistent 可以存储为 arg 值。
这种特殊情况不需要您从数据库中的 arg 值重新创建对象,否则,您需要像 __repr__
was intended to be(但通常不这样做):
this should look like a valid Python expression that could be used to recreate an object with the same value (given an appropriate environment).
我会在这里使用键值存储,其中键可以是函数对象的 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)
>>>