Откладывание вызова функции в аргументе в python 3

Я пытаюсь добавить своего рода кеш в дорогостоящую функцию, используя фиксированный словарь. Что-то вроде этого:

def func(arg):
    if arg in precomputed:
        return precomputed[arg]
    else:
        return expensive_function(arg)

Теперь было бы немного чище, если бы я мог сделать что-то подобное, используя dict.get() значений по умолчанию:

def func(arg):
    return precomputed.get(arg, expensive_function(arg))

Проблема в том, что expensive_function() запускается независимо от успеха precomputed.get(), так что мы получаем весь жир за отсутствие вкуса.

Есть ли способ отложить вызов expensive_function() здесь, чтобы он вызывался только в случае сбоя precomputed_get()?


person TheEnvironmentalist    schedule 21.02.2020    source источник
comment
Я думаю, что самым питоническим способом будет try: return pre[arg] except KeyError: return exp_fun(arg)… Почему он должен быть однострочным?   -  person deceze♦    schedule 21.02.2020
comment
В зависимости от ввода декоратор lru_cache может быть альтернативным решением. .   -  person Matthias    schedule 21.02.2020
comment
@deceze Почему try-catch такой питонический? Я видел, что это рекомендуется в других сценариях, но я немного застрял на том, чтобы не попасть в код обработки ошибок, когда это возможно, из других языков. Разве это не влияет на производительность?   -  person TheEnvironmentalist    schedule 21.02.2020
comment
Вы можете прочитать Idiomatic Python: EAFP против LBYL   -  person Matthias    schedule 21.02.2020
comment
@wim Использование здесь генератора вряд ли сделает код более читаемым в этом случае: P   -  person Amadan    schedule 21.02.2020
comment
@wim Я думаю, что это превратилось в более глубокую дискуссию о том, что такое pythonic, и я многому учусь. Я могу только представить, что другие выиграют от обсуждения. Я рад отредактировать вопрос, чтобы лучше подчеркнуть этот факт, если хотите, дайте мне знать или не стесняйтесь вносить изменения самостоятельно, если хотите.   -  person TheEnvironmentalist    schedule 21.02.2020
comment
Я имею в виду, что решения в этом другом вопросе на самом деле вам не помогут, но я могу сказать вам, что, имея 8 лет опыта, простое и читаемое выражение if, которое у вас изначально было, было лучшим способом: )   -  person wim    schedule 21.02.2020
comment
@TheEnvironmentalist Вы пишете Python, производительность уже покинула здание (если только вы не обманываете, используя библиотеки с поддержкой C, такие как numpy). Вы, вероятно, не в курсе, но, например. каждый раз, когда вы используете цикл for, вы полагаетесь на исключение для остановить это. Так что бороться с исключениями в Python бесполезно.   -  person Amadan    schedule 21.02.2020


Ответы (1)


Если вам нужна чистота, я предлагаю использовать библиотеку, а не изобретать велосипед:

from functools import lru_cache

@lru_cache
def expensive_function(arg):
    # do expensive thing
    pass

Теперь все обращения к expensive_function запоминаются, и вы можете вызывать его, не занимаясь кешем самостоятельно. (Если вас беспокоит потребление памяти, вы можете даже ограничить размер кеша.)

Чтобы ответить на буквальный вопрос, Python не имеет возможности создавать функции или макросы, которые лениво оценивают свои параметры, как это делают Haskell или Scheme. Единственный способ отложить вычисления в Python — это обернуть их функцией или генератором. Это будет менее, а не более читаемо, чем ваш исходный код.

person Amadan    schedule 21.02.2020