db中的memoization的数据结构

时间:2015-10-31 09:09:31

标签: python database-design data-structures memoization

缓存(保存/存储/记忆)的最佳数据结构是什么,这么多功能导致数据库。 假设函数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个函数。

2 个答案:

答案 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_nameargumentsresult其中'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_callsfunc_nameargs_combo_idarg_name_idxarg_value。该表的每一行将存储一个arg,用于该函数调用args的一个组合。另一个表格func_resultsfunc_nameargs_combo_idresult。您还可以进一步规范化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)
>>>