Функция-декоратор, которая кеширует значение декорируемой функции

Есть задание: "Напишите функцию-декоратор, которая сохранит (закеширует) значение декорируемой функции.Если декорируемая функция будет вызвана повторно с теми же параметрами — декоратор должен вернуть сохранённый результат, не выполняя функцию." И подсказка к заданию: "Создайте словарь и при каждом вызове декоратора сохраняйте в нём аргументы задекорированной функции. При каждом вызове проверяйте, не было ли уже аналогичного вызова. Если был — верните результат прошлого вызова, если не было — верните результат декорируемой функции и одновременно сохраните этот результат в словарь. Ключом для каждой записи словаря может быть аргумент декорируемой функции."

Ну и, собственно, уже готовый код задания, в котором мне нужно дописать функцию def cache_args(func):

import time
from functools import wraps


def time_check(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        execution_time = round(time.time() - start_time, 1)
        print(f'Время выполнения функции {func.__name__}: {execution_time} с.')
        return result

    return wrapper


def cache_args(func):
    # Здесь код декоратора
    ...


@time_check
@cache_args
def long_heavy(num):
    time.sleep(1)
    return num * 2


print(long_heavy(1))
# Время выполнения функции long_heavy: 1.0 с.
# 2
print(long_heavy(1))
# Время выполнения функции long_heavy: 0.0 с.
# 2
print(long_heavy(2))
# Время выполнения функции long_heavy: 1.0 с.
# 4
print(long_heavy(2))
# Время выполнения функции long_heavy: 0.0 с.
# 4
print(long_heavy(2))
# Время выполнения функции long_heavy: 0.0 с.
# 4

Я понимаю, что в данном случае ключ словаря - это аргумент функции: *args. Значение словаря - это значение самой функции. Но как это все написать правильно синтаксически, не знаю.


Ответы (1 шт):

Автор решения: vadim vaduxa
import time
from functools import wraps

def time_check(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        execution_time = round(time.time() - start_time, 1)
        print(f'Время выполнения функции {func.__name__}: {execution_time} с.')
        return result
    return wrapper

def cache_args(func):
    dt = {}
    @wraps(func)
    def wrapper(*args):
        if args not in dt:  # При каждом вызове проверяйте, не было ли уже аналогичного вызова
            dt[args] = func(*args)
        return dt[args]
    return wrapper


@time_check
@cache_args
def long_heavy(n1, n2):
    time.sleep(1)
    return n1 + n2

print(long_heavy(1, 2))
print(long_heavy(1, 2))

out:

Время выполнения функции long_heavy: 1.0 с.
3
Время выполнения функции long_heavy: 0.0 с.
3
→ Ссылка