Значение по умолчанию не то же самое, что переданное значение?
Ради интереса решил попробовать написать какое-то подобие аналога @cache из functools. Декоратор проверяет, выполнялась ли уже такая функция с такими аргументами. Если да - возвращает уже когда-то посчитанный результат.
def decor(func):
cashe = dict()
def wrapper(*args, **kwargs):
key = f"{func}"
for item in args:
key += str(hash(str(item)))
for key_item in kwargs:
key += str(hash(str(key_item)))
if key not in cashe:
print('Создал!')
result = func(*args, **kwargs)
cashe[key] = result
else:
print('Взял из кеша!')
result = cashe[key]
return result
return wrapper
В целом, думаю, можно посидеть и сделать лаконичнее/быстрее/короче, но суть сейчас не в этом.
Когда я использую
@decor
def check(a, b, c=0):
return a + b + c
check(1, 5) # Создал
check(1, 5) # Взял из кеша
Всё отрабатывает так, как я ожидаю, но когда
check(1, 5, 0) # Создал
check(1, 5) # Создал
Почему так происходит? c ведь неизменяема и в обоих случаях равна 0, но почему функции считаются разными?
Ответы (1 шт):
Потому что ваш декоратор смотрит только на фактические параметры. В него приходят конкретно два набора параметров: (1, 5, 0) и (1, 5) - это два разных наборов параметров, соответственно и ключи в кэше разные. Параметры по умолчанию в набор фактических параметров добавляются только при вызове самой функции, а не при вызове "обертки", добавленной декоратором.
Нужно использовать интроспекцию, и вытаскивать информацию о параметрах функции по умолчанию, их добавлять в фактические аргументы.
Пример с передачей именованного параметра:
import inspect
def get_default_args(func):
# Код функции взят из ответа: https://stackoverflow.com/a/12627202
signature = inspect.signature(func)
return {
k: v.default
for k, v in signature.parameters.items()
if v.default is not inspect.Parameter.empty
}
def decor(func):
cache = dict()
def wrapper(*args, **kwargs):
key = ""
kwargs_with_defaults = get_default_args(func)
kwargs_with_defaults.update(kwargs)
for item in args:
key += str(hash(str(item)))
# У именованных параметром берем и имена, и значения, отсортированные по возрастанию имени
for key_item in sorted(kwargs_with_defaults.items(), key=lambda x: x[0]):
key += str(hash(str(key_item)))
if key not in cache:
print('Создал!')
result = func(*args, **kwargs)
cache[key] = result
else:
print('Взял из кеша!')
result = cache[key]
return result
return wrapper
@decor
def check(a, b, c=0):
return a + b + c
check(1, 5, c=0)
check(1, 5)
Вывод:
Создал!
Взял из кеша!
С позиционными параметрами тоже можно реализовать, возможно чуть позже допишу (но это не точно).