Комментарии 20
Будет ещё круче!
Чтобы с самого начала было всем и совсем понятно, что правила русского языка к этому опусу не имеют никакого отношения!
P.S. От "читаймо", конечно, чуть глаз не лопнул, но я их просто руками придерживал, пока не долистал до однострочника, ничего страшного.
В питоне метод .get у dict возвращает значение, а если ключа не существует - None.
Так же есть or, которая возвращает 1ое, если оно приводится к True, иначе 2ое.
Таким обзразом None or default_value
вернёт default_value
.
Это очень полезно для работы со словарями, ТК вы можете использовать такую связку
data[key] = data.get(key) or func(args)
return data[key]
Превратить это в функцию, или если вам угодно, лямбда функцию, не составит труда, однако читаемость и лаконичность кода значительно повышаются.
Если вам очень важно число обращений к data, лучше использовать if key in data.
data[key] = data.get(key) or func(args)
Тут еще надо учитывать, что в словаре могут быть «пустые» значения ('', 0, False, [] и т.д.), тогда этот вариант будет делать не совсем то, что требуется
Согласен, не учел. Тогда это можно сделать так:
data[key] = func(args) if data.get(key) is None else data[key]
Что уже не так лаконично (с ? : выглядело бы лучше, но это уже другой вопрос), но все ещё сокращает код.
Вообще-то dict.get
принимает два параметра. Второй — значение по умолчанию.
Так что можно написать data[key] = data.get(key, func(args))
key = f"{func.__name__}{args}"
# проверяем кэшировли дунную функцию с аргументами
if args in data:
return data.get(key)
А разве не if key in data? Разве можно по части ключа в словаре что-то найти?
В рамках академического изыскания хорошо еще почитать про stdlib: @functools.cache.
Вот моя версия, надеюсь, вы это вынесите:
def cached(func, data={}, _s=object()): return lambda *args: ((data.__setitem__(f"{func.__name__}({args})", func(*args)), data[f"{func.__name__}({args})"])[1]) if data.get(f"{func.__name__}({args})", _s) is _s else data[f"{func.__name__}({args})"]
Зачем здесь всё?
data - собственно кеш, так потому что в 1 строку
_s как sentinel тоже самое, но чтобы различать None после вызова и отсутствие результата
Можно было бы впихнуть в параметры лямбду по получению ключа, но пока зачем.
(data.__setitem__("<>", func(*args)), data["<>")[1]
Это создание кортежа, в котором происходит запись результата функции и тут же рядом достается, потому что не везде есть walrus operator и вообще я его не очень люблю, зато __setitem__ является обычной функцией и возвращает ничего, то есть является валиднвм выражением в отличие от data["<>"] = func(*args).
def cache(func): return lambda *a, **k: func.__dict__.setdefault(f'{a}:{k}', f'{a}:{k}' in func.__dict__ or func(*a, **k)) or func.__dict__[f'{a}:{k}']
3 действия
- вызываем функцию только если элемента нет в кеше
f'{a}:{k}' in func.__dict__ or func(*a, **k))
- записываем новое значение, если его нет в кеше
func.__dict__.setdefault(f'{a}:{k}', ...
- возвращаем значение из кеша
or func.__dict__[f'{a}:{k}']
Непрактичный python — пишем декоратор в одну строку