Вызов декорированной функции дважды возвращает только декоратор, а не функцию - PullRequest
0 голосов
/ 11 января 2020

Вот мой код, который определяет временный декоратор:

from functools import wraps, lru_cache

def timed(fn):
    from time import perf_counter

    @wraps(fn)
    def inner(*args,**kwargs):
        start = perf_counter()
        result = fn(*args,**kwargs)
        end = perf_counter()
        timer = end - start

        fs = '{} took {:.3f} microseconds'
        print(fs.format(fn.__name__, (end - start) * 1000000))

        return result
    return inner

Вот определение функции:

@timed
@lru_cache
def factorial(n):
    result = 1
    cache = dict()
    if n < 2:
        print('Calculating factorial for n > 1')

        result = 1
        print(f'factorial of {result} is {result}')

    else:
        for i in range(1,n+1):
            if i in cache.items():
                result = cache[i]
                #print(f'factorial of {i} is {result}')
            else:
                result *= i
                cache[i] = result

        print(f'factorial of {i} is {result}')
            #print(f'{cache}')
    return result

Вот вызовы функций:

факториал (3)

факториал (10)

факториал (10)

Вот вывод

factorial of 3 is 6
factorial took 32.968 microseconds
factorial of 10 is 3628800
factorial took 11.371 microseconds
**factorial took 0.323 microseconds**

Вопрос: почему, когда я вызываю factorial (10) во второй раз, распечатка отсутствует?

Ответы [ 2 ]

2 голосов
/ 11 января 2020

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

Когда Вы вызываете factorial(10) во второй раз , функция не вызывается, а значение выбирается из кэша. И именно поэтому второй вызов 35 раз быстрее - потому что функция даже не вызывается, и это точное назначение functools.lru_cache.

1 голос
/ 11 января 2020

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

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

@lru_cache
def factorial_math(n):
    result = 1
    for i in range(2, n):
        result *= i
    return result

@timed
def factorial(n):
    result = factorial_math(n)
    print(f'factorial of {n} is {result}')
    return result
...