Исключения и порядок обработки except в Python
@python_quizРазберем этот квиз
Коротко: в конструкции try/except Python перебирает обработчики сверху вниз и выбирает первый подходящий. В этом примере это определяет, что именно будет напечатано.
Исходный код

Что произойдет при запуске
В блоке try явно возбуждается ValueError. Перечисленные except-обработчики проверяются по очереди:
- Первый except ловит исключения типа
TypeErrorилиKeyError.ValueErrorк этим типам не относится — этот обработчик пропускается. - Второй except ловит Exception и все его подклассы (а
ValueError— как раз подкласс Exception), поэтому он сработает. Внутри него вычисляется type(e).__name__и печатается имя класса исключения.
Итоговый вывод программы:

Почему именно так — подробности
- В конструкции except (A, B) as e перечисляются классы исключений. Обработчик сработает, если возбужденное исключение является экземпляром любого из перечисленных классов (или их подклассов).
- Исключения ищутся сверху вниз: как только найден подходящий обработчик, остальные не проверяются.
- except Exception ловит большинство обычных исключений, потому что большинство встроенных исключений в Python наследуются от Exception.
- Есть исключения, которые НЕ наследуются от Exception (например, SystemExit, KeyboardInterrupt и
BaseExceptionнапрямую). Они не будут пойманы обработчиком except Exception.
Разбор вариантов (логика, а не метки)
- "Напечатает A": это произошло бы, если бы было возбуждено исключение типа
TypeErrorилиKeyError(или их подкласс). В данном случае возбуждаетсяValueError, так что этот вариант неверен. - "Напечатает имя класса исключения (
ValueError)": соответствует реальному поведению: второй обработчик печатает type(e).__name__, т.е. "ValueError". - "Напечатает Exception": такого вывода в коде нет — вторым обработчиком печатается имя конкретного класса исключения, а не строка "Exception".
- "Будет необработанное исключение": неверно для текущего кода и
ValueError, потому что есть обработчик except Exception, который поймаетValueError. Необработанным он был бы только если бы не было подходящего обработчика (например, если возбуждаемое исключение — SystemExit или KeyboardInterrupt и нет обработчикаBaseException).
Эксперименты — несколько вариантов
Попробуем несколько вариантов, чтобы увидеть различия:

И пример с исключением, не наследующимся от Exception:

В последнем случае except Exception не сработает, потому что KeyboardInterrupt наследуется напрямую от BaseException, а не от Exception.
Вывод
Порядок и список классов в блоках except критичны. Если первый обработчик не соответствует типу исключения, Python продолжит поиск — и ValueError в данном коде будет пойман общим обработчиком Exception, который и выведет имя класса исключения.