Ошибки и исключения
Ошибки в программировании неизбежны, однако важно научиться распознавать и грамотно реагировать на них. При одниз ошибках код вовсе не запустится, а при других аварийно завершится при выполнении строки с ошибкой. Однако Python – очень дружелюбный язык, и всегда старается подсказать, в чем проблема.
Синтаксические ошибки
Самый частый гость у начинающих программистов – это синтаксические ошибки, например, опечатки или пропущенные символы. Python в этом плане очень строгий и требует, чтобы код был написан по определенным правилам. Если вы допустите такую ошибку, Python даже не попытается запустить вашу программу и сразу сообщит, что именно ему не понравилось.
Например, выведем сообщение на экран, но пропустим закрывающую скобку при вызове функции print()
:
print("Это сообщение написано не очень правильно"
Запустив этот код, вы увидите примерно такое сообщение о синтаксической ошибке:
Traceback (most recent call last):
File "/home/irina/projects/task.py", line 2
print("Это сообщение написано не очень правильно"
^
SyntaxError: '(' was never closed
Python выводит трассировку (англ. traceback) с информацией не только о самой ошибке, но и о её месте. Так line 2
означает, что исключение вызвал код в строке 2, а стрелочка ^
показывает что именно не так – забыта закрывающая скобка.
Если в вашем коде есть хотя бы одна синтаксическая ошибка, то он не будет запущен.
Исключения
Но бывают ошибки другого рода – те, которые возникают уже во время работы программы. Представьте, что ваша программа должна разделить одно число на другое, но второе число оказалось нулем. В математике на ноль делить нельзя, и Python тоже об этом знает. Такие ошибки, возникающие в процессе выполнения программы, называются исключениями.
Давайте напишем программу, которая запрашивает у пользователя два числа и выводит на экран результат деления первого числа на второе:
number1 = float(input("Пожалуйста, введите первое число: "))
number2 = float(input("Пожалуйста, введите второе число: "))
result = number1 / number2
print(f"{number1} / {number2} = {result}")
Допустим, пользователь ввёл числа 1
и 2
, тогда программа завершится без ошибок:
Пожалуйста, введите первое число: 1
Пожалуйста, введите второе число: 2
1 / 2 = 0.5
Но если в качестве второго числа был введён 0
, то выполнение программы прервётся на строке result = number1 / number2
и будет вызвано исключение ZeroDivisionError (с англ. – Ошибка деления на нуль):
Пожалуйста, введите первое число: 1
Пожалуйста, введите второе число: 0
Traceback (most recent call last):
File "/home/irina/toss a coin/Архив/Учебник.py", line 3, in <module>
result = number1 / number2
~~~~~~~~^~~~~~~~~
ZeroDivisionError: float division by zero
Эта же программа может прерваться и из-за другого исключения, если пользователь вместо числа введёт строку, которую невозможно преобразовать в число:
Пожалуйста, введите первое число: Цифра 1
Traceback (most recent call last):
File "/home/irina/toss a coin/Архив/Учебник.py", line 1, in <module>
number1 = int(input("Пожалуйста, введите первое число: "))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: invalid literal for int() with base 10: 'Цифра 1'
Так как строку "Цифра 1"
нельзя преобразовать в целое число, выполнение программы прерывается на первой же строке и вызывается исключение ValueError (англ. – Ошибка значения).
Типы исключений
В ходе работы программы могут возникать самые разные ошибки, для которых Python предоставляет большой набор встроенных исключений. Также существует возможность разработки собственных исключений, но для этого нужно, как минимум, освоить объектно-ориентированное программирование.
Давайте рассмотрим некоторые распространённые стандартные исключения.
1. NameError (с англ. – Ошибка имени) – используется объект (переменная или функция), который еще не были создан.
print(username)
# Ошибка: NameError: name 'username' is not defined
Переменной name
еще не было присвоено значение, поэтому вызывается исключение.
2. TypeError (с англ. – Ошибка типа) – операция применяется к объекту не того типа:
"Яблоки" + 10
# Ошибка: TypeError: can only concatenate str (not "int") to str
Строки и числа нельзя складывать напрямую.
3. ValueError (с англ. – Ошибка значения) – функции передан аргумент правильного типа, но недопустимого значения:
int("Число 25")
# Ошибка: ValueError: invalid literal for int() with base 10: 'Число'
Строку
невозможно преобразовать в число, так как она содержит недопустимые буквенные символы."Число 25
"
4. ZeroDivisionError (с англ. – Ошибка деления на ноль) – попытка деления на ноль:
result = 10.5 / 0
# Ошибка: ZeroDivisionError: float division by zero
Это связано с невозможностью деления на ноль в математике.
С другими типами исключений мы познакомимся по мере изучения языка Python.
Обработка исключений
Чаще всего мы можем предположить, какие исключения могут возникнуть в программе. Например, пользователь не обязательно введёт число или захочет поделить на ноль. Избежать аварийного завершения программы в таких случаях позволяет специальная конструкция try/except
, способная эффективно перехватывать и обрабатывать исключения:
try:
действия_которые_могут_привести_к_исключению
except ТипИсключения:
действия_если_возникло_исключение
Если код внутри блока try
(с англ. – попытка) вызвал исключение, тип которого совпадает с исключением, указанным после оператора except
(с англ. – исключение), то выполнение блока try
останавливается, и контроль переходит к соответствующему блоку except
.
Давайте перепишем пример программы для деления двух чисел, используя try/except
:
try:
number1 = int(input("Пожалуйста, введите первое целое число: "))
number2 = int(input("Пожалуйста, введите второе целое число: "))
result = number1 / number2
print(f"{number1} / {number2} = {result}")
except ZeroDivisionError:
print("На ноль делить нельзя!")
except ValueError:
print("Пожалуйста, введите целое число!")
Теперь если пользователь захочет поделить на ноль, программа всё равно продолжит работу:
Пожалуйста, введите первое число: 1
Пожалуйста, введите второе число: 0
На ноль делить нельзя!
Также такой код обрабатывает ввод некорректного значения и исключение ValueError:
Пожалуйста, введите первое целое число: пять
Пожалуйста, введите целое число!
В одном блоке except
можно обрабатывать сразу несколько типов исключений. Для этого достаточно перечислить их в скобках через запятую:
try:
number1 = int(input("Пожалуйста, введите первое число: "))
number2 = int(input("Пожалуйста, введите второе число: "))
result = number1/number2
print(f"{number1} / {number2} = {result}")
except (ValueError, ZeroDivisionError):
print("Упс! Что-то пошло не так!")
И хотя явное указание типов ожидаемых исключений является хорошей практикой, это не является обязательным:
try:
number1 = int(input("Пожалуйста, введите первое число: "))
number2 = int(input("Пожалуйста, введите второе число: "))
result = number1/number2
print(f"{number1} / {number2} = {result}")
except:
print("Упс! Что-то пошло не так!")
В таком случае блок except
обрабатывает все возникающие исключения:
Пожалуйста, введите первое число: 1
Пожалуйста, введите второе число: 0
Упс! Что-то пошло не так!
Пожалуйста, введите первое целое число: пять
Упс! Что-то пошло не так!
Но для того, чтобы понимать, какое исключение было перехвачено, с помощью конструкции Exception as e
можно сохранить информацию об исключении в переменной e
:
try:
number1 = int(input("Пожалуйста, введите первое число: "))
number2 = int(input("Пожалуйста, введите второе число: "))
result = number1/number2
print(f"{number1} / {number2} = {result}")
except Exception as e:
print(f"Упс! Что-то пошло не так: {e}")
Значение переменной e
может быть выведено на экран:
Пожалуйста, введите первое число: 1
Пожалуйста, введите второе число: 0
Упс! Что-то пошло не так: division by zero
Хотя этот подход гарантирует, что программа не выйдет из строя ни при каких ошибках, его следует использовать с осторожностью, чтобы не замаскировать реальные ошибки.
Необязательный блок else
Иногда вам может понадобиться выполнить какой-то код, если в блоке try
не произошло исключений. Для этих целей предназначен необязательный блок else
:
try:
number1 = int(input("Пожалуйста, введите первое число: "))
number2 = int(input("Пожалуйста, введите второе число: "))
result = number1/number2
print(f"{number1} / {number2} = {result}")
except ValueError:
print("Вы должны ввести число!")
except ZeroDivisionError:
print(f"На ноль делить нельзя!")
else:
print(f"Увеличим результат деления в 1000 раз: {result * 1000}")
Здесь результат деления будет увеличен в 1000 раз, если код в блоке try
был выполнен полностью:
Пожалуйста, введите первое число: 1
Пожалуйста, введите второе число: 2
1 / 2 = 0.5
Увеличим результат деления в 1000 раз: 500.0
Но если возникло исключение и был выполнен код из блока except
, то код в блоке else
игнорируется:
Пожалуйста, введите первое число: 1
Пожалуйста, введите второе число: 0
На ноль делить нельзя!
Такая структура позволяет не помещать всю логику в блок try
.
Необязательный блок finally
Независимо от того, возникли ли исключения, иногда нужно гарантированно выполнить некоторые операции (например, закрыть файл). Для этого служит дополнительный блок finally
, который выполняется всегда, независимо от того, произошло исключение в блоке try
или нет.
try:
number1 = int(input("Пожалуйста, введите первое число: "))
number2 = int(input("Пожалуйста, введите второе число: "))
result = number1/number2
print(f"{number1} / {number2} = {result}")
except ValueError:
print("Вы должны ввести число!")
except ZeroDivisionError:
print(f"На ноль делить нельзя!")
else:
print(f"Увеличим результат деления в 1000 раз: {result * 1000}")
finally:
print("Спасибо за использование нашего калькулятора!")
Так сообщение из блока finally
будет выведено на экран, как в случае корректной работы программы и отсутствия исключений:
Пожалуйста, введите первое число: 1
Пожалуйста, введите второе число: 2
1 / 2 = 0.5
Спасибо за использование нашего калькулятора!
Так и если было перехвачено какое-то исключение:
Пожалуйста, введите первое число: 1
Пожалуйста, введите второе число: 0
На ноль делить нельзя!
Спасибо за использование нашего калькулятора!
Рекомендации по обработке исключений
1. Перехватывайте только ожидаемые исключения – не обрабатывайте все возникающие исключения одним способом без необходимости, так как это может скрыть неожиданные ошибки.
2. Сообщайте об ошибках понятно – сообщения об ошибках должны быть информативными для пользователя или программиста, чтобы они понимали, что пошло не так и как это исправить.
3. Не злоупотребляйте обработкой исключений – используйте обработку исключений только там, где это действительно необходимо. Не стоит оборачивать в try
весь написанный код.
4. Делайте блоки try
краткими – чем меньше кода находится внутри блока try
, тем легче понять, какая именно операция могла вызвать исключение.
Обработка исключений – это важный навык, который делает ваши программы более надежными и устойчивыми к неожиданным ситуациям. Не бойтесь ошибок, а учитесь их предвидеть и обрабатывать.
1. В следующем фрагменте кода есть синтаксическая ошибка. Найдите и исправьте её.
age = int(input("Сколько тебе лет? "))
if age >= 18
print("Ты совершеннолетний и можешь голосовать.")
else:
print("Ты ещё несовершеннолетний, тебе нужно подождать.")
2. Рассмотрите следующую трассировку ошибки:
Traceback (most recent call last):
File "/home/user/script.py", line 5, in calculate
result = number1 / number2
ZeroDivisionError: division by zero
На какой строке кода возникло исключение? Что является причиной возникновения ошибки?
3. Напишите программу, которая запрашивает у пользователя число и пытается преобразовать его в целое число. Используйте конструкцию try/except
для обработки случая, если пользователь введет не число.
4. В каком случае будет выполнен блок else
в конструкции try/except/else
?
5. Напишите программу, которая запрашивает у пользователя его возраст, и если возраст больше или равен 16
, то выводит сообщение "Доступ разрешён"
, иначе – "Доступ запрещён"
.
Если введённую строку нельзя преобразовать в число, то выведите сообщение "Ошибка: пожалуйста, введите целое число"
.
После обработки ввода, независимо от того, было ли введенное значение корректным числом или нет, выведите сообщение "Проверка возраста завершена"
.
Пример входных данных | Пример выходных данных |
---|---|
16 | Доступ разрешён Проверка возраста завершена |
14 | Доступ запрещён Проверка возраста завершена |
Всегда восемнадцать | Ошибка: пожалуйста, введите целое число Проверка возраста завершена |