Исключения
Иерархия исключений, обработка, finally и пользовательские исключения.
12 вопросов
JuniorТеорияОчень частоЧто такое обработка исключений и как возбудить исключение?
Что такое обработка исключений и как возбудить исключение?
Это механизм реакции на ошибки выполнения: try/except ловит их, raise возбуждает. Возбуждают экземпляр (или класс, конструируемый автоматически) BaseException или его подкласса. Непойманные распространяются вверх по стеку.
Типичные ошибки
- ✗Думать, что через
raiseможно возбуждать произвольные объекты вроде строк илиint, а не подклассыBaseException - ✗Считать, что
raiseвсегда аварийно завершает программу и не может быть пойман окружающим обработчиком
Уточняющие вопросы
- →В чём разница между возбуждением класса и возбуждением экземпляра?
- →Что произойдёт, если ни один обработчик не поймает распространяющееся исключение?
JuniorТеорияЧастоКак определить пользовательское исключение в Python?
Как определить пользовательское исключение в Python?
Создайте подкласс Exception (не BaseException), по соглашению с суффиксом Error в имени. Можно добавить атрибуты или свой __init__ для переноса данных. Затем возбуждайте через raise MyError("msg"), как встроенное.
Типичные ошибки
- ✗Наследовать
BaseExceptionвместоException, из-за чегоexcept Exceptionего не ловит - ✗Забывать, что пользовательское исключение может нести сообщение и доп. атрибуты
Уточняющие вопросы
- →Как построить иерархию связанных пользовательских исключений?
- →Что собственный
__init__должен вызвать уsuper(), чтобы сохранитьargs?
JuniorТеорияЧастоКакова иерархия исключений на самом верху?
Какова иерархия исключений на самом верху?
BaseException — корень иерархии. Exception происходит от него и служит базой для обычных ошибок. Но SystemExit, KeyboardInterrupt и GeneratorExit происходят напрямую от BaseException, поэтому except Exception их не ловит.
Типичные ошибки
- ✗Полагать, что
except ExceptionловитSystemExitилиKeyboardInterrupt - ✗Считать
BaseExceptionиExceptionвзаимозаменяемыми
Уточняющие вопросы
- →Почему
SystemExitиKeyboardInterruptнамеренно держат внеException? - →Какой базовый класс мысленно ловит голая ветка
except:?
JuniorТеорияЧастоЗачем использовать try/finally без except?
Зачем использовать try/finally без except?
Блок finally выполняется всегда — успешно ли завершился try, возбудил исключение или вернул значение — поэтому его применяют для гарантированной очистки: закрытия файлов, освобождения блокировок, при этом позволяя исключению распространяться непойманным.
Типичные ошибки
- ✗Считать, что
finallyпропускается при исключении вtry— он выполняется всегда - ✗Думать, что
finallyпоглощает исключение, а не позволяет ему распространяться
Уточняющие вопросы
- →Что произойдёт, если сам
finallyсодержитreturnилиraise? - →Как оператор
withсвязан сtry/finally?
MiddleТеорияЧастоВ каком порядке нужно записывать несколько обработчиков except?
В каком порядке нужно записывать несколько обработчиков except?
Обработчики проверяются сверху вниз, и срабатывает лишь первый подходящий, поэтому более специфичные исключения должны идти перед общими базами, иначе общий перекроет их. Голый except: должен стоять последним.
Типичные ошибки
- ✗Ставить общий базовый обработчик перед специфичным подклассом, перекрывая последний
- ✗Считать, что срабатывают все подходящие обработчики, а не только первый
Уточняющие вопросы
- →Чем
except (TypeError, ValueError)отличается от двух отдельных веток? - →Почему Python советует избегать голой ветки
except:?
MiddleТеорияИногдаЧто делает блок else в операторе try?
Что делает блок else в операторе try?
Блок else выполняется, только если try завершился без исключения. В нём держат код, который не должен быть защищён except, оставляя охраняемый try минимальным. Он выполняется перед finally.
Типичные ошибки
- ✗Думать, что
elseвыполняется при исключении, а не при его отсутствии - ✗Путать
elseсfinallyи считать, что он выполняется всегда
Уточняющие вопросы
- →Зачем переносить код в
else, а не оставлять его внутриtry? - →В каком порядке выполняются
elseиfinallyпри чистом проходе?
MiddleДебаггингИногдаПочему этот обработчик ValueError недостижим?
Почему этот обработчик ValueError недостижим?
except Exception проверяется первым и ловит и ValueError тоже (это подкласс), поэтому обработчик ValueError недостижим (Python даже предупреждает). Ставьте конкретные исключения перед общими. Также избегайте голого except: — он проглатывает KeyboardInterrupt / SystemExit.
Типичные ошибки
- ✗Писать общий обработчик перед конкретным
- ✗Думать, что Python выбирает наиболее точный обработчик независимо от порядка
- ✗Считать, что
ValueErrorне подклассException
Уточняющие вопросы
- →О чём Python предупреждает для недостижимого предложения
except? - →Почему голый
except:не рекомендуется даже при верном порядке?
MiddleТеорияИногдаКак поймать исключение, выполнить действие и повторно возбудить то же самое?
Как поймать исключение, выполнить действие и повторно возбудить то же самое?
Используйте голый raise без аргумента внутри блока except — это повторное возбуждение текущего обрабатываемого исключения с сохранением исходного traceback. Запись raise exc вместо этого сбросила бы этот traceback.
Типичные ошибки
- ✗Писать
raise excвместо гологоraise, что портит traceback - ✗Думать, что голый
raiseвозбуждает обобщённую ошибку, а не текущее исключение
Уточняющие вопросы
- →Чем
raise NewError from excотличается от гологоraise? - →Что делает голый
raise, если сейчас не обрабатывается никакое исключение?
MiddleТеорияИногдаЧто такое предупреждения и как выдать одно?
Что такое предупреждения и как выдать одно?
Предупреждения сигнализируют о нефатальных проблемах, не останавливая выполнение. Warning — подкласс Exception; вызов warnings.warn(message, category=UserWarning) выдаёт одно. Фильтр warnings может подавлять их или повышать до ошибок.
Типичные ошибки
- ✗Полагать, что предупреждение останавливает выполнение, как исключение
- ✗Забывать, что
Warningсам происходит отException
Уточняющие вопросы
- →Как повысить конкретную категорию предупреждения до жёсткой ошибки?
- →Что регулирует аргумент
stacklevelуwarnings.warn?
SeniorТеорияИногдаЧто такое сцепление исключений (__context__ против __cause__)?
Что такое сцепление исключений (__context__ против __cause__)?
Если исключение возбуждено при обработке другого, Python неявно связывает старое через __context__. Явное raise New from old задаёт __cause__ как прямую причину; raise New from None подавляет контекст.
Типичные ошибки
- ✗Считать, что второе исключение перезаписывает и теряет исходное
- ✗Путать неявный
__context__с явным__cause__отraise ... from
Уточняющие вопросы
- →Когда вы сознательно используете
raise New from None? - →Как
__suppress_context__меняет печатаемый traceback?
SeniorТеорияИногдаЧто происходит с исключением, которое не ловит ни один обработчик?
Что происходит с исключением, которое не ловит ни один обработчик?
Оно распространяется наружу к ближайшему try/except; если никто не ловит, интерпретатор печатает traceback в sys.stderr и завершается. Но SystemExit завершает тихо, а возбуждённое в __del__ лишь печатает «Exception ignored».
Типичные ошибки
- ✗Думать, что непойманное исключение молча игнорируется и выполнение продолжается
- ✗Полагать, что
SystemExitпечатает полный traceback, как обычная ошибка
Уточняющие вопросы
- →Как настроить происходящее через
sys.excepthook? - →Почему исключение в
__del__лишь сообщается, а не распространяется?
SeniorТеорияРедкоКогда SyntaxError действительно можно поймать во время выполнения?
Когда SyntaxError действительно можно поймать во время выполнения?
SyntaxError в главном модуле прерывает программу до выполнения, поэтому там его не поймать. Он ловится в рантайме: при импорте модуля с плохим синтаксисом или при eval/exec над некорректной строкой. SyntaxError — подкласс Exception.
Типичные ошибки
- ✗Полагать, что
SyntaxErrorнельзя поймать ни при каких условиях - ✗Считать, что
try/exceptможет пропустить некорректный синтаксис в текущем файле
Уточняющие вопросы
- →Почему парсер заваливает весь модуль до запуска любой строки?
- →Какой родственный класс наследует
IndentationError?