Какая лучшая практика использования try-except в функциях?

В каких случаях нужно использовать блоки try-except в функциях, а в каких их не рекомендованно или вообще нельзя использовать?

Как лучше перехватывать и обрабатывать исключения и ошибки - с блоком или без? Интересует best practice для разработчика.


Уточняющий комментарий.

Я и курсы проходил и сам использую try exception во всех функциях. Недавно при работе с deno (JS) опытный разработчик, сказал, чтобы я не использовал try-catch вообще, кроме случая код я не могу предусмотреть все возможные ошибки.

В связи с этим у меня возник вопрос - а на сколько правильно и оправданно использования try-except во всех функциях в которых идет вызов каких-то библиотек?

Пример кода функции с try except. На сколько он корректный и оптимальный с точки зрения обработки исключений?

async def GetPrices(symbol, exchange, limit=10):

    sellPrice = None
    buyPrice = None

    while True:
        try:
            orderBook = await exchange.fetchOrderBook(symbol, limit=limit)
            bidsPrice, bidsAmount = map(list, zip(*orderBook['bids'])) 
            asksPrice, asksAmount = map(list, zip(*orderBook['asks'])) 
            
            buyPrice = np.average(bidsPrice, weights = bidsAmount)
            sellPrice = np.average(asksPrice, weights = asksAmount)
            break

        except ccxt.RequestTimeout as e:
            log.error("Exchange %s  %s  %s", exchange.name, '[' + type(e).__name__ + ']', str(e)[0:200])
            # will retry
        except ccxt.DDoSProtection as e:
            log.error("Exchange %s  %s  %s", exchange.name, '[' + type(e).__name__ + ']', str(e)[0:200])
            # will retry
        except ccxt.ExchangeNotAvailable as e:
            log.error("Exchange %s  %s  %s", exchange.name, '[' + type(e).__name__ + ']', str(e)[0:200])
            # will retry
        except ccxt.ExchangeError as e:
            log.error("Exchange %s  %s  %s", exchange.name, '[' + type(e).__name__ + ']', str(e)[0:200])
            break  # won't retry
        except Exception as e:
            log.error("Exchange %s  %s  %s", exchange.name, '[' + type(e).__name__ + ']', str(e)[0:200])
            break  # won't retry

    return sellPrice, buyPrice

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

Автор решения: MaxU

После того как вы уточнили вопрос в комментарии, я думаю я смогу дать несколько советов по правильному, на мой взгляд, использованию обработки исключений.

По-моему перехватывать нужно только заведомо известные исключения, которые мы хотим явно обработать и для которых мы очень четко себе представляем, что делать после того как исключение сработало.

Во всех остальных случаях нужно либо не обрабатывать исключения или же опять вызывать raise для повторного вызова исключительной ситуации из блока except .... Часто начинающие разработчики пишут обработчики исключительных ситуаций в таком стиле:

# NOTE: DONT'T DO THIS !!!
try:
    #do_something...
except Exception as e:
    print("видимо что-то пошло не так")

Это очень плохой стиль, т.к. после такой обработки ошибки мы совершенно слепы и не понимаем, что пошло не так и в каком месте и как с этим бороться. Такая обратка ошибок приносит гораздо больше вреда, чем пользы.

Пример осмысленной обработки исключений:

def to_float(num_str: str) -> float:
    try:
        return float(str(num_str).replace(",", "."))
    except ValueError as exc:
        print(f"Значение [{num_str}] не может быть преобразовано в вещественное число")

тесты:

In [52]: to_float(1.23)
Out[52]: 1.23

In [53]: to_float("3.14")
Out[53]: 3.14

In [54]: to_float("1,23")
Out[54]: 1.23

In [55]: to_float("nan")
Out[55]: nan

In [57]: to_float("1e-3")
Out[57]: 0.001

In [74]: to_float("10a")
Значение [10a] не может быть преобразовано в вещественное число
→ Ссылка