Что такое исключение?
Исключения — это события, которые нарушают нормальный поток выполнения программы. Они возникают, когда программа сталкивается с ошибкой, которую она не может обработать в обычном режиме. Например, попытка деления на ноль или преобразование строки к числу когда она не является числом могут вызвать исключения.
Если говорить доступным языком, то исключения это попытка выполнить то, что у языка выполнить не получится.
Иерархия исключений выглядит вот так:
Начнем со знакомства с самыми обычными исключениями, которые вы увидите в Python. Обратите внимание на то, что ошибка и исключение – два разных слова, описывающих одно и то же в контексте обработки исключений.
Их прям много, детально разберем с чем мы можем столкнуться на данном этапе
Exception
– то, на чем фактически строятся все остальные ошибки;
KeyboardInterrupt
– возникает, когда пользователь нажимает клавишу прерывания (обычно Delete
или Ctrl+C
);
NameError
– возникает, когда локальное или глобальное имя не найдено;
SyntaxError
— возникает, когда синтаксическая ошибка встречается синтаксическим анализатором;
TypeError
– возникает, когда операция или функция применяется к объекту несоответствующего типа. Связанное значение
представляет собой строку, в которой приводятся подробные сведения о несоответствии типов;
ValueError
– возникает, когда встроенная операция или функция получают аргумент, тип которого правильный, но
неправильно значение, и ситуация не может быть описана более точно, как при возникновении IndexError
(Дальше опишу это
человекочитаемым языком, не пугаемся);
ZeroDivisionError
– возникает, когда второй аргумент операции division или modulo равен нулю;
По мере изучения разных аспектов и типов данных мы будем разбирать и какие исключения могут с ними произойти
Эта ошибка возникает когда вы пытаетесь обратиться к переменной которой не существует.
some_variable = 123
print(variable_doesnt_exist)
Вы увидите ошибку:
NameError: name 'variable_doesnt_exist' is not defined
Эта ошибка явно означает, что вам надо проверить имена своих переменных и скорее всего вы или где-то опечатались, или просто явно совершили ошибку.
Эта ошибка возможна в любом случае когда вы пытаетесь набрать что-либо, что не допустимо в python-е и он вас просто не понимает, не знает что именно ему делать.
5 +
*5
Или например:
if:
print('aaa')
Вариантов очень много, любые синтаксические ошибки будут вызывать это исключение. (Кстати, из-за чего ошибка в последнем примере?)
Ошибка:
SyntaxError: invalid syntax
Эта ошибка всегда обозначает, что вы где-то не то напечатали, внимательно посмотрите на то место о котором пытается сообщить вам python и постарайтесь исправить ошибку.
Происходит в случае когда вы пытаетесь произвести не допустимые действия с объектами разного типа, например, сложить строку и число
"a" + 5
Ошибка:
TypeError: can only concatenate str (not "int") to str
Это исключение пока что вы будете видеть только в случае когда пытаетесь преобразовать что-то что невозможно преобразовать. На самом деле это исключение случается гораздо чаще, что вы и увидите при дальнейшем изучении
int("abc")
Ошибка:
ValueError: invalid literal for int() with base 10: 'abc'
Все очень просто, появляется при попытке делить на 0
1 / 0
Ошибка:
ZeroDivisionError: division by zero
Обработка исключений в Python – это очень просто. Потратим немного времени и напишем несколько примеров, которые их вызовут. Мы начнем с одной из самых элементарных проблем: деление на ноль.
1 / 0
За обработку исключений в python отвечают ключевые слова try/except.
try:
1 / 0
except ZeroDivisionError:
print("You cannot divide by zero!")
Если мы обратимся к урокам элементарной математики, то вспомним, что на ноль делить нельзя. В Python данная операция вызовет ошибку, как мы можем видеть в примере выше. Чтобы поймать ошибку, мы завернем операцию в оператор try/except.
В блоке try должен выполняться код в котором мы ожидаем какое-либо исключение. И если исключение происходит, то код перестает выполняться, и переходит к блоку/блокам except, и пытается понять произошло ли то исключение, которое описано в блоке except
Блоков except может быть любое количество, так же в одном блоке except может быть больше одного исключения, если вам для разных ошибок нужны одинаковые действия, такие исключения должны быть указаны в скобках и через запятую. Рассмотрим такой пример:
bottles_of_beer = 10
try:
amount_of_people = int(input("Please input amount of people:"))
bottle_per_person = bottles_of_beer / bottles_of_beer
print(bottle_per_person)
except (ValueError, ZeroDivisionError):
print('Incorrect amount of people!')
В этом случае когда мы введем не число, или введем 0, програма продолжит работу, потому что мы обработали исключение и описали что код должен сделать
Или вот такой:
bottles_of_beer = 10
try:
amount_of_people = int(input("Please input amount of people:"))
bottle_per_person = bottles_of_beer / bottles_of_beer
print(bottle_per_person)
except ValueError:
print('Your input is not a number')
except ZeroDivisionError:
print('You are trying divide to zero')
Как и тут, мы тоже все обработали
Комбинировать можно любое количество исключений.
А что произойдет если сделать вот так?
try:
bottles_of_beer = 10
amount_of_people = int(input("Please input amount of people:"))
bottle_per_person = bottles_of_beer / bottles_of_beer
print(bottle_per_person)
except SyntaxError:
print('Your input is not a number')
Произойдет следующее, если мы не введем буквы или 0, то ничего страшного, ведь исключение не произошло. А если ввели, то python попытается обработать исключение, но не найдет подходящий эксепшен, и просто завершить програму с ошибкой, как будто исключение и не было обработано
Есть еще один способ поймать ошибку:
try:
1 / 0
except:
print("You cannot divide by zero!")
# ЭТО СРАБОТАЕТ, НО ТАК ДЕЛАТЬ НЕЛЬЗЯ
На жаргоне Python это известно как "голое исключение", что означает, что будут найдены вообще все исключения. Причина,
по которой так делать не рекомендуется, заключается в том, что вы не узнаете, что именно за исключение вы выловите.
Когда у вас возникло что-то в духе ZeroDivisionError
, вы хотите выявить фрагмент, в котором происходит деление на
ноль. В написанном выше коде вы не можете указать, что именно вам нужно выявить. Хорошие программисты четко указывают
какую ошибку, ожидают, и что с ней делать
Оператор finally
очень прост в использовании. Давайте взглянем на нижеизложенный пример:
try:
value = int("ABC")
except ValueError:
print("A ValueError occurred!")
finally:
print("The finally statement has executed!")
Что произойдет? Часть finally
выполниться вообще всегда, хоть попали в исключение хоть нет.
Оператор try/except
также имеет блок else
. Он работает только в том случае, если в вашем коде нет ни единой ошибки.
Давайте потратим немного времени и взглянем на парочку примеров:
try:
value = int(input("Please enter value"))
except ValueError:
print("A ValueError occurred!")
else:
print("No error occurred!")
В этом примере, если мы введем число, то исключения не случится, и это именно тот случай когда мы попадем в else
.
Целиком вся конструкция состоит из 4 блоков
try:
value = int(input("Please enter value"))
except ValueError:
print("A ValueError occurred!")
else:
print("No error occurred!")
finally:
print("The finally statement ran!")
В данном коде работают и оператор else
, и finally
если введете число или except
и finally
если букву. Большую
часть времени вы не будете сталкиваться с оператором else
, используемым в том или ином коде, который следует за
оператором try/except
, если ни одна ошибка не была найдена. Единственное полезное применение оператора else
, которое
я видел, — это когда вы хотите запустить вторую часть кода, в которой может быть ошибка. Конечно, если ошибка возникает
в else
, то она не будет поймана.
Если в вашем коде какие-либо данные не соответствуют вашим ожиданиям, вы всегда можете вызвать исключение, если вам это
необходимо, для этого используется ключевое слово raise
.
if odds % 2 != 1:
raise ValueError("Did not get an odd number")
Эта конструкция и ключевое слово нам понадобится после того как мы разберем функции, пока я могу показать только одно полезное применение.
Технически всегда можно обработать исключение и вызвать его же еще раз. Зачем такое может быть надо? В случае когда нам не надо предотвращать ошибку, а только записать о том что ошибка случилась (так часто делается), тогда можно записать информацию и вызвать эту же ошибку еще раз
try:
5 / 0
except ZeroDivisionError as e:
raise ZeroDivisionError("Got an error", e)
в переменной e
будет храниться вся информация об исключении, и таким способом мы вызовем нужный нам тип исключения, с
нашим комментарием и всей системной информацией, делать это через ключевое слово as
не обязательно, можно и без этого,
но такой способ дает нам больше возможностей.
В качестве практики нужно взять все задания из прошлых двух лекций и попробовать покрыть весь код возможными исключениями.