Короткий ответ
Один except (TypeA, TypeB): перехватывает любое исключение из перечисленных типов и обрабатывает его одним и тем же кодом, тогда как несколько отдельных except TypeA: / except TypeB: позволяют обработать каждый тип исключения своей отдельной, различающейся логикой.
Как это работает подробнее
except (ValueError, KeyError) as exc: проверяет, является ли возникшее исключение экземпляром любого из перечисленных классов (включая их подклассы), и если да — выполняет общий блок кода с одной и той же переменной exc, независимо от того, какой конкретно тип сработал. Порядок проверки except-блоков в try важен: Python проверяет их сверху вниз и выполняет первый подходящий, остальные блоки для этого исключения не рассматриваются.
Пример кода
example.py
def parse_amount(raw):
try:
data = {"amount": raw}
return int(data["amount"])
except (KeyError, TypeError, ValueError) as exc:
print(f"Некорректные данные платежа: {exc!r}")
return None
print(parse_amount("150")) # 150
print(parse_amount("abc")) # Некорректные данные платежа: ValueError(...) -> None
print(parse_amount(None)) # Некорректные данные платежа: TypeError(...) -> NoneЧто использовать на практике
- Практика. Группировку в один except используют, когда реакция на разные ошибки действительно одинакова (например, "залогировать и вернуть обобщённую ошибку клиенту"). Если же разным типам ошибок требуется разная реакция (повторить запрос при таймауте, но не повторять при ошибке валидации) — предпочтительнее отдельные except-блоки, расположенные от более специфичных типов к более общим, чтобы не "затенить" специфичную обработку более общим перехватом, стоящим раньше.
Подводные камни
- Риски. Ставят
except Exception:первым, из-за чего более специфичные except-блоки ниже становятся недостижимым кодом (многие линтеры это подсвечивают). Также группируют в один except типы ошибок, которые на самом деле требуют разной обработки, из-за чего теряется важная информация о причине сбоя при логировании или ответе клиенту API.