PythonJuniorTechnical
Что плохого в except: без указания типа исключения?
bare except ловит BaseException, включая KeyboardInterrupt, SystemExit и GeneratorExit. Это маскирует ошибки, мешает Ctrl+C завершить программу и оставляет состояние неконсистентным. Правильно — ловить конкретный тип или Exception.
Что именно происходит
Конструкция except: без типа эквивалентна except BaseException:. В Python иерархия выглядит так:
BaseException
├── SystemExit # sys.exit()
├── KeyboardInterrupt # Ctrl+C
├── GeneratorExit # close() на генераторе
└── Exception
├── ValueError
├── TypeError
├── OSError
└── ...
Прикладные ошибки наследуются от Exception. SystemExit, KeyboardInterrupt, GeneratorExit намеренно лежат выше — чтобы их нельзя было случайно поймать.
Чем плох bare except
- Ctrl+C не работает: KeyboardInterrupt проглатывается, и пользователь не может остановить процесс.
- Скрытые баги: любая опечатка вроде
NameErrorмолча проглатывается, ветка ошибки не залогирована. - Нарушает контракт sys.exit: SystemExit не доходит до интерпретатора, процесс не завершается.
- Ломает генераторы: ловит GeneratorExit, и
finallyв генераторе не успевает корректно отработать. - Скрывает MemoryError и asyncio.CancelledError (последний на 3.8+ тоже под BaseException).
Как писать правильно
# Хорошо — конкретный тип
try:
value = int(raw)
except ValueError:
value = 0
# Допустимо — Exception на границе слоя с обязательным логом и re-raise или явная обработка
try:
result = external_api_call()
except Exception:
logger.exception("external_api_call failed")
raise # либо вернуть default, но не молча
# Если действительно нужно поймать всё (cleanup в __exit__),
# делать это явно и пропускать SystemExit/KeyboardInterrupt
try:
risky()
except (KeyboardInterrupt, SystemExit):
raise
except BaseException:
logger.exception("unexpected")
raise
Подводные камни
except Exception:без re-raise и без логирования — почти так же плохо, как bare except.- Catch-all в retry-loop без exponential backoff и без проверки исключения превращается в busy loop, который жрёт CPU и логи.
- В asyncio глотать
BaseException= глотатьCancelledError, и Task будет считаться нормально завершённой. - Линтеры (
ruff E722,flake8 E722,pylint W0702) ловят bare except — включите их в CI.
Common mistakes
- Говорить, что bare except просто некрасивый стиль.
- Не знать BaseException.
- Ловить исключение без действия.
What the interviewer is testing
- Объясняет KeyboardInterrupt/SystemExit.
- Предлагает конкретные exception types.
- Понимает логирование и re-raise.