Исключения и порядок обработки except в Python

Исключения и порядок обработки except в Python

@python_quiz

Разберем этот квиз

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

Исходный код

python

Что произойдет при запуске

В блоке try явно возбуждается ValueError. Перечисленные except-обработчики проверяются по очереди:

  1. Первый except ловит исключения типа TypeError или KeyError. ValueError к этим типам не относится — этот обработчик пропускается.
  2. Второй 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).

Эксперименты — несколько вариантов

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

python

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

python

В последнем случае except Exception не сработает, потому что KeyboardInterrupt наследуется напрямую от BaseException, а не от Exception.

Вывод

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

Report Page