Исключения являются событиями, способными изменить ход выполнения программы, они позволяют перепрыгнуть через фрагмент программы произвольной длины. Исключения в языке Python возбуждаются автоматически, когда программный код допускает ошибку, а также могут возбуждаться и перехватываться самим программным кодом. Обрабатываются исключения четырьмя инструкциями.
try/except — перехватывает исключения, возбужденные интерпретатором или вашим программным кодом, и выполняет восстановительные операции.
try/finally выполняет заключительные операции независимо от того, возникло исключение или нет.
raise — дает возможность возбудить исключение программно.
assert — дает возможность возбудить исключение программно, при выполнении определенного условия.
Благодаря исключениям программа может перейти к обработчику исключения за один шаг, отменив все вызовы функций. Обработчик исключений (инструкция try
) ставит метку и выполняет некоторый программный код. Если затем где-нибудь в программе возникает исключение, интерпретатор немедленно возвращается к метке, отменяя все активные вызовы функций, которые были произведены после установки метки.
Обработка ошибок. Интерпретатор возбуждает исключение всякий раз, когда обнаруживает ошибку во время выполнения программы. Программа может перехватывать такие ошибки и обрабатывать их или просто игнорировать. Если ошибка игнорируется, интерпретатор выполняет действия, предусмотренные по умолчанию, – он останавливает выполнение программы и выводит сообщение об ошибке. Если такое поведение по умолчанию является нежелательным, можно добавить инструкцию try, которая позволит перехватывать обнаруженные ошибки и продолжить выполнение программы после инструкции try.
Уведомления о событиях Исключения могут также использоваться для уведомления о наступлении некоторых условий, что устраняет необходимость передавать куда-либо флаги результата или явно проверять их. Например, функция поиска может возбуждать исключение в случае неудачи, вместо того чтобы возвращать целочисленный признак в виде результата (и надеяться, что этот признак всегда будет интерпретироваться правильно).
Обработка особых ситуаций. Некоторые условия могут наступать так редко, что было бы слишком расточительно предусматривать проверку наступления таких условий с целью их обработки. Нередко такие проверки необычных ситуаций можно заменить обработчиками исключений.
Заключительные операции. Как будет показано далее, инструкция try/finally позволяет гарантировать выполнение завершающих операций независимо от наличия исключений.
Необычное управление потоком выполнения. И, наконец, так как исключения – это своего рода оператор «goto», их можно использовать как основу для экзотического управления потоком выполнения программы.
Предположим, что у нас имеется следующая функция:
def fetcher(obj, index): return obj[index]
s = "some_string" fetcher(s, 3)
Поскольку наш программный код не перехватывает это исключение явно, оно возвращает выполнение на верхний уровень программы и вызывает обработчик исключений по умолчанию, который просто выводит стандартное сообщение об ошибке. К настоящему моменту вы наверняка видели в своих программах подобные сообщения об ошибках. Они включают тип исключения, а также диагностическую информацию – список строк и функций, которые были активны в момент появления исключения.
fetcher(s, 20)
Иногда это совсем не то, что нам требуется. Например, серверные программы обычно должны оставаться активными даже после появления внутренних ошибок.
Если вам требуется избежать реакции на исключение по умолчанию, достаточно просто перехватить исключение, обернув вызов функции инструкцией try
:
try: fetcher(s, 20) except IndexError: print("got exception") print("continuing")
Инструкции try
могут включать блоки finally
. Эти блоки выглядят точно так же, как обработчики except
. Комбинация try/finally
определяет завершающие действия, которые всегда выполняются «на выходе», независимо от того, возникло исключение в блоке try
или нет.
На практике комбинацию try/except
удобно использовать для перехвата и восстановления после исключений, а комбинацию try/finally
– в случаях, когда необходимо гарантировать выполнение заключительных действий независимо от того, возникло исключение в блоке try
или нет. Например, комбинацию try/except
можно было бы использовать для перехвата ошибок, возникающих в импортированной библиотеке, созданной сторонним разработчиком, а комбинацию try/finally
– чтобы гарантировать закрытие файлов и соединений с сервером.
try/finally
можно использовать вместо with/as
.
try: f = open("some_file.txt", "w") # что то делаем и получаем ошибку except IOError as err: print("Получаем ошибку и печатаем её: ", err) finally: # но файл закрыть не забываем f.close() print("Этот код выполнится")
В Python есть два больших типа исключений. Первый — это исключения из стандартной библиотеки в Python, второй тип исключений — это пользовательские исключения. Они могут быть сгенерированы и обработаны самим программистом при написании программ на Python. Давайте посмотрим на иерархию исключений в стандартной библиотеке Python. Все исключения наследуются от базового класса BaseException
:
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- AssertionError
+-- AttributeError
+-- LookupError
+-- IndexError
+-- KeyError
+-- OSError
+-- SystemError
+-- TypeError
+-- ValueError
Существуют несколько системных исключений, например, SystemExit
(генерируется, если мы вызвали функцию OSExit
), KeyboardInterrupt
(генерируется, если мы нажали сочетание клавиш Ctrl + C
) и так далее. Все остальные исключения генерируется от базового класса Exception
. Именно от этого класса нужно порождать свои исключения.
Попробуем преобразовать строку в целое число, видим ValueError
:
int("asdf")
Получим TypeError
при попытке сложить целое число со строкой:
1 + "10"
Чтобы отлавливать любые ошибки программы, возникающеие в коде, можно отлавливать ошибку Exception
try: """ some code """ except Exception: print("got all exceptions") print("continuing")
Внимание! Не стоит отливливать BaseException
или KeyboardInterrupt
, т.к. иногда вы даже не сможете выйти из программы. Python добускает возможность не указывать тип ошибки после except
, что равнозначно except BaseException
.
while True: try: raw = input("введите число: ") number = int(raw) break except: # не указали тип исключения, значит, обрабатываем все print("некорректное значение")
Исключения можно вызывать самостоятельно при помощи ключевого слова raise
.
try: raw = input("введите число: ") if not raw.isdigit(): raise ValueError except ValueError: print("некорректное значение!")
Говоря об исключениях, нельзя не затронуть инструкцию assert
. По умолчанию, если выполнить инструкцию assert
с логическим выражением, результат которого равен True
, ничего не произойдет. Но если попробовать выполнить инструкцию assert
с логическим выражением, которое равно False
, то будет сгенерировано исключение AssertionError
. Также мы можем передать дополнительную строку в сам объект AssertionError
.
user_input = input("Введите ваше имя: ") assert user_input, "Пустая строка!" # введено ли имя print("Выполнится только если введено")
Исключения AssertionError
предназначены скорее для программистов. При написании наших программ на этапе разработки мы должны видеть, что делаем что-то не так (например, передали в функцию некорректное значение). Не нужно, например, обрабатывать пользовательский ввод и пытаться обработать исключение AssertionError
блоком try except
. Если таких мест будет очень много, то это затронет и производительность нашей программы.
Однако, есть возможность отключить все инструкции assert при помощи флага −O
. Тогда AssertionError
не будет сгенерирована. Этим и отличаются исключения AssertionError
от обычных пользовательских исключений и исключений стандартной библиотеки.
Можно определять пользовательские исключения, чем часто пользуются различные пакеты. Рассмотрим, напирмер, requests.
import os import requests ex_path = os.path.split(requests.__file__)[0] + "/exceptions.py" print(ex_path)
with open(ex_path) as f: print(f.read())
url = "https://github.com/not_found" try: response = requests.get(url, timeout=30) response.raise_for_status() except requests.Timeout: print("ошибка timeout, url:", url) except requests.HTTPError as err: code = err.response.status_code print("ошибка url: {0}, code: {1}".format(url, code)) except requests.RequestException: print("ошибка скачивания url: ", url) else: print(response.content)
Комментарии