кэширование последних k результатов функции в python - PullRequest
0 голосов
/ 09 декабря 2018

Я хочу написать функцию, которая принимает функцию f с одним аргументом и целое число k и возвращает функцию, которая ведет себя так же, как f, за исключением того, что она кэширует последние k результатов f.

ДляНапример, если memoize - это функция, за которой мы следуем, и пусть mem_f = memoize (f, 2), то:

    mem_f(arg1) -> f(arg1) is computed and cached  
    mem_f(arg1) -> f(arg1) is returned from cache  
    mem_f(arg2) -> f(arg2) is computed and cached  
    mem_f(arg3) -> f(arg3) is computed and cached, and f(arg1) is evicted

То, что я сделал, это:

def memoize(f,k):
    cache = dict()

    def mem_f(*args):
        if args in cache:
            return cache[args]
        result = f(*args)
        cache[args]= result
        return result 
    return mem_f

ThisФункция возвращает результат из кеша, и если он не в кеше, он вычисляется и кешируется.Тем не менее, мне не ясно, как кэшировать только последние k результатов f? Я новичок, любая помощь будет оценена.

Ответы [ 3 ]

0 голосов
/ 09 декабря 2018

Вы можете просто использовать functools.lru_cache для кэширования.Я принимаю параметр maxsize для управления объемом кэширования:

from functools import lru_cache

@lru_cache(maxsize=2)
def test(n):
    print("calling function")
    return n * 2

print(test(2))
print(test(2))
print(test(3))
print(test(3))
print(test(4))
print(test(4))
print(test(2))

результаты:

вызывающая функция
4
4
вызывающая функция
6
6
вызывающая функция
8
8
вызывающая функция
4

0 голосов
/ 09 декабря 2018

Продолжая замечательное предложение Марка Мейера, вот как выглядит решение, используя lru_cache и терминологию вашего вопроса:

from functools import lru_cache


def memoize(f, k):
    mem_f = lru_cache(maxsize=k)(f)
    return mem_f


def multiply(a, b):
    print("Called with {}, {}".format(a, b))
    return a * b


def main():
    memo_multiply = memoize(multiply, 2)
    print("Answer: {}".format(memo_multiply(3, 4)))
    print("Answer: {}".format(memo_multiply(3, 4)))
    print("Answer: {}".format(memo_multiply(3, 7)))
    print("Answer: {}".format(memo_multiply(3, 8)))


if __name__ == "__main__":
    main()

Результат:

Called with 3, 4
Answer: 12
Answer: 12
Called with 3, 7
Answer: 21
Called with 3, 8
Answer: 24
0 голосов
/ 09 декабря 2018

Решение

Вы можете исправить код, используя OrderedDict, например:

from collections import OrderedDict

def memoize(f, k):
    cache = OrderedDict()

    def mem_f(*args):
        if args in cache:
            return cache[args]
        result = f(*args)
        if len(cache) >= k:
            cache.popitem(last=False)
        cache[args]= result
        return result 
    return mem_f,cache

Тестирование

def mysum(a, b):
    return a + b

mysum_cached,cache = memoize(mysum, 10)
for i in range(100)
    mysum_cached(i, i)

print(cache)

Вывод:

OrderedDict([((90, 90), 180), ((91, 91), 182), ((92, 92), 184), ((93, 93), 186), ((94, 94), 188), ((95, 95), 190), ((96, 96), 192), ((97, 97), 194), ((98, 98), 196), ((99, 99), 198)])

Эта версия memoize, вероятно, будет хорошо работать для вашего собственного кода.Однако для производственного кода (т. Е. Кода, на который должны полагаться другие люди), вам, вероятно, следует использовать стандартную библиотечную функцию (functools.lru_cache), которую предлагает Марк Мейер.

...