Проблема с iferror(1/0,0)
заключается в том, что аргументы функции оцениваются до ее ввода (это имеет место в большинстве языков программирования, за исключением одного большого исключения - Haskell). Независимо от того, что делает iferror
, 1/0
запускается первым и выдает ошибку.
Мы должны как-то задержать оценку 1/0
, чтобы это произошло внутри функции, в контексте блока try
. Одним из способов является использование строки (iferror('1/0', 1)
), которую iferror
может затем eval
. Но по возможности следует избегать eval
, и есть более легкая альтернатива: тела функций не оцениваются до тех пор, пока не будет вызвана функция, поэтому мы можем просто обернуть наше выражение в функцию и передать следующее:
def iferror(success, failure, *exceptions):
try:
return success()
except exceptions or Exception:
return failure
def my_expr():
return 1/0
print(iferror(my_expr, 42))
42
Важнейшей частью здесь является то, что мы не вызываем my_expr
напрямую. Мы передаем его как функцию в iferror
, который затем вызывает success()
, что в итоге приводит к выполнению return 1/0
.
Единственная проблема заключается в том, что нам пришлось извлечь аргумент функции (1/0
) из обычного потока кода в отдельное определение функции, которому мы должны были дать имя (даже при том, что оно использовалось только один раз).
Этих недостатков можно избежать, используя lambda
, что позволяет нам определять встроенные функции с одним выражением:
def iferror(success, failure, *exceptions):
try:
return success()
# ^^
except exceptions or Exception:
return failure
print(iferror(lambda: 1/0, 42))
# ^^^^^^^
42
[ Демо ]
По сравнению с вашей первоначальной попыткой потребовались только два изменения: обернуть выражение в lambda:
, что задерживает оценку, и использовать ()
в try: return success()
для вызова лямбда-выражения, которое запускает оценку тела функции.