Структура данных памятки в БД

Какова лучшая структура данных для кэширования (сохранения / сохранения / запоминания), поэтому многие функции приводят к базе данных. Предположим, функция 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)

Я вижу ответы на вопрос : какую структуру таблицы следует использовать для хранения параметров и результатов запомненных функций в реляционной базе данных? но кажется, что это решает проблему только одной функции, в то время как у меня есть около 500 функций.

2 ответа

Решение

Вариант А

Вы можете использовать структуру в связанном ответе, ненормализованную с количеством столбцов = максимальное количество аргументов среди 500 функций. Также необходимо добавить столбец для имени функции.

Тогда вы могли бы сделать SELECT * FROM expensive_func_results WHERE func_name = 'calc_regress' AND arg1 = ind_key AND arg2 = dep_key and arg3 = count, так далее.

Конечно, это не очень хороший дизайн для использования. Для той же функции, вызываемой с меньшим количеством параметров, столбцы с нулевыми значениями / несоответствиями должны игнорироваться; в противном случае вы получите несколько строк результатов.

Вариант Б

Создайте таблицу / структуру как func_name, arguments, result где 'arguments' - это всегда словарь kwargs или позиционные аргументы, но не смешанный для каждой записи. Даже при том, что kwargs dict хранится в виде строки, порядок ключей-> значений в нем не является предсказуемым / непротиворечивым, даже если это одни и те же аргументы. Поэтому вам нужно будет заказать его перед преобразованием в строку и ее сохранением. Когда вы хотите запросить, вы будете использовать SELECT * FROM expensive_func_results WHERE func_name = 'calc_regress' AND arguments = 'str(kwargs_dict)', где str(kwargs_dict) это то, что вы установите программно. Это также может быть установлено на результат inspect.getargspec, (или же inspect.getcallargs) хотя вам придется проверять последовательность.

Вы не сможете выполнять запросы на комбинации аргументов, если вы не предоставите все аргументы для запроса или частичное совпадение с LIKE,

Вариант С

Нормализовано полностью: одна таблица func_calls как func_name, args_combo_id, arg_name_idx, arg_value, В каждой строке таблицы будет храниться один аргумент для одной комбинации из аргументов вызова этой функции. Еще один стол func_results как func_name, args_combo_id, result, Вы также можете нормализовать дальше func_name быть сопоставленным с func_id,

В этом порядке порядок аргументов ключевых слов не имеет значения, так как вы будете выполнять внутреннее объединение для выбора каждого параметра. Этот запрос должен быть построен программно или выполнен с помощью хранимой процедуры, поскольку количество соединений, необходимых для извлечения всех параметров, определяется количеством параметров. Ваша функция выше имеет 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 в БД, в противном случае вам нужно будет сделать __str__ или же __repr__ как способ __repr__ предполагалось (но обычно не делается):

это должно выглядеть как допустимое выражение Python, которое можно использовать для воссоздания объекта с тем же значением (при условии соответствующей среды).

Я бы использовал здесь хранилище значений ключей, где ключ может быть конкатенацией 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)
>>> 
Другие вопросы по тегам