Python — как мне запомнить частичный объект?

У меня есть набор функций, которые принимают целые числа и функции в качестве аргументов. Я хотел бы запомнить их.

Я знаю, что используя это решение , я мог бы использовать pickle для кодирования обоих наборов аргументов и запоминания закодированных значений. Однако в данном конкретном случае аргументы функции большие и постоянные, и я бы предпочел не занимать пространство lru_cache аргументом функции, который в ходе выполнения программы никогда не меняется.

Есть ли способ запомнить частичную функцию, где я исправил аргументы функции и получил частичный объект, который принимает только хешируемые аргументы? Я не могу понять, как использовать декоратор functools.lru_cache как функцию.

Вот что я пробовал на игрушечном примере. Это не работает; биномиальное дерево по-прежнему повторно посещает узлы.

import functools
import logging


logging.basicConfig(level=logging.DEBUG)


def binomial_tree(x, y, fn):
    logging.debug(f"binomial_tree({x}, {y})")
    """Note: this does not recombine, and we can't memoize function."""
    if x == 10:
        return fn(x, y)
    else:
        return 0.5 * binomial_tree(x + 1, y, fn) + 0.5 * binomial_tree(x + 1, y + 1, fn)


def memoize_fn(fn):
    @functools.lru_cache(maxsize=None)
    def inner(*args, **kwargs):
        return fn(*args, **kwargs)
    return inner

memoized_binomial_tree = memoize_fn(functools.partial(binomial_tree, fn=lambda x, y: 10 * x * y))
print(memoized_binomial_tree(0, 0))

person MikeRand    schedule 14.09.2019    source источник
comment
Проблема здесь в том, что ваше запоминание частичного объекта не влияет на два вызова binomial_tree(x + 1, y, fn) и binomial_tree(x + 1, y + 1, fn).   -  person sanyassh    schedule 14.09.2019
comment
Правильно. Я думаю, я был бы в порядке, если бы просто запомнил binomial_tree. Но я не хочу в данном случае.   -  person MikeRand    schedule 14.09.2019
comment
Аргумент функции действительно никогда не меняется во время выполнения программы? Это может быть глупый вопрос, но какой смысл делать партиал вместо использования класса или глобальной переменной для функции? Или я слишком серьезно отношусь к примеру с игрушкой?   -  person MSeifert    schedule 14.09.2019
comment
Библиотека, в которую я хочу перейти, чтобы выбрать одну из нескольких разных версий fn. Но да, изменение глобального будет работать. Я просто не люблю глобалки.   -  person MikeRand    schedule 14.09.2019


Ответы (1)


Вот способ запомнить ваш игрушечный пример с помощью binomial_tree без кодирования и запоминания аргументов функции:

import functools
import logging


logging.basicConfig(level=logging.DEBUG)


def create_binomial_tree(fn):
    @functools.lru_cache(maxsize=None)
    def binomial_tree(x, y):
        logging.debug(f"binomial_tree({x}, {y})")
        if x == 10:
            return fn(x, y)
        else:
            return 0.5 * binomial_tree(x + 1, y) + 0.5 * binomial_tree(x + 1, y + 1)
    return binomial_tree


memoized_binomial_tree = create_binomial_tree(fn=lambda x, y: 10 * x * y)
print(memoized_binomial_tree(0, 0))

Может быть, это может быть применимо в вашем реальном случае использования?

person sanyassh    schedule 14.09.2019
comment
Итак, я имитирую частичное закрытие, но вставляю кэширование между ними. Это работает. - person MikeRand; 14.09.2019