Skip to content

Latest commit

 

History

History
315 lines (222 loc) · 15.4 KB

lesson3.md

File metadata and controls

315 lines (222 loc) · 15.4 KB

Лекция 3. Обработка исключений.

Что такое исключение?

Исключения — это события, которые нарушают нормальный поток выполнения программы. Они возникают, когда программа сталкивается с ошибкой, которую она не может обработать в обычном режиме. Например, попытка деления на ноль или преобразование строки к числу когда она не является числом могут вызвать исключения.

Если говорить доступным языком, то исключения это попытка выполнить то, что у языка выполнить не получится.

Иерархия исключений выглядит вот так:

Начнем со знакомства с самыми обычными исключениями, которые вы увидите в Python. Обратите внимание на то, что ошибка и исключение – два разных слова, описывающих одно и то же в контексте обработки исключений.

Основные исключения на данном этапе

Их прям много, детально разберем с чем мы можем столкнуться на данном этапе

Exception – то, на чем фактически строятся все остальные ошибки;

KeyboardInterrupt – возникает, когда пользователь нажимает клавишу прерывания (обычно Delete или Ctrl+C);

NameError – возникает, когда локальное или глобальное имя не найдено;

SyntaxError — возникает, когда синтаксическая ошибка встречается синтаксическим анализатором;

TypeError – возникает, когда операция или функция применяется к объекту несоответствующего типа. Связанное значение представляет собой строку, в которой приводятся подробные сведения о несоответствии типов;

ValueError – возникает, когда встроенная операция или функция получают аргумент, тип которого правильный, но неправильно значение, и ситуация не может быть описана более точно, как при возникновении IndexError (Дальше опишу это человекочитаемым языком, не пугаемся);

ZeroDivisionError – возникает, когда второй аргумент операции division или modulo равен нулю;

По мере изучения разных аспектов и типов данных мы будем разбирать и какие исключения могут с ними произойти

В каких случаях мы уже можем столкнуться с этими исключениями?

NameError

Эта ошибка возникает когда вы пытаетесь обратиться к переменной которой не существует.

some_variable = 123
print(variable_doesnt_exist)

Вы увидите ошибку:

NameError: name 'variable_doesnt_exist' is not defined

Эта ошибка явно означает, что вам надо проверить имена своих переменных и скорее всего вы или где-то опечатались, или просто явно совершили ошибку.

SyntaxError

Эта ошибка возможна в любом случае когда вы пытаетесь набрать что-либо, что не допустимо в python-е и он вас просто не понимает, не знает что именно ему делать.

5 +
*5

Или например:

if:
    print('aaa')

Вариантов очень много, любые синтаксические ошибки будут вызывать это исключение. (Кстати, из-за чего ошибка в последнем примере?)

Ошибка:

SyntaxError: invalid syntax

Эта ошибка всегда обозначает, что вы где-то не то напечатали, внимательно посмотрите на то место о котором пытается сообщить вам python и постарайтесь исправить ошибку.

TypeError

Происходит в случае когда вы пытаетесь произвести не допустимые действия с объектами разного типа, например, сложить строку и число

"a" + 5

Ошибка:

TypeError: can only concatenate str (not "int") to str

ValueError

Это исключение пока что вы будете видеть только в случае когда пытаетесь преобразовать что-то что невозможно преобразовать. На самом деле это исключение случается гораздо чаще, что вы и увидите при дальнейшем изучении

int("abc")

Ошибка:

ValueError: invalid literal for int() with base 10: 'abc'

ZeroDivisionError

Все очень просто, появляется при попытке делить на 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

Оператор finally очень прост в использовании. Давайте взглянем на нижеизложенный пример:

try:
    value = int("ABC")
except ValueError:
    print("A ValueError occurred!")
finally:
    print("The finally statement has executed!")

Что произойдет? Часть finally выполниться вообще всегда, хоть попали в исключение хоть нет.

Оператор else

Оператор try/except также имеет блок else. Он работает только в том случае, если в вашем коде нет ни единой ошибки. Давайте потратим немного времени и взглянем на парочку примеров:

try:
    value = int(input("Please enter value"))
except ValueError:
    print("A ValueError occurred!")
else:
    print("No error occurred!")

В этом примере, если мы введем число, то исключения не случится, и это именно тот случай когда мы попадем в else.

Вся конструкция целиком try-except-else-finally:

Целиком вся конструкция состоит из 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

Если в вашем коде какие-либо данные не соответствуют вашим ожиданиям, вы всегда можете вызвать исключение, если вам это необходимо, для этого используется ключевое слово 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 не обязательно, можно и без этого, но такой способ дает нам больше возможностей.

В качестве практики нужно взять все задания из прошлых двух лекций и попробовать покрыть весь код возможными исключениями.