Обработка исключений: Java, JS, PHP, Python, C++

Что такое исключения в программировании

Большинство наших проектов устроены так: когда во время работы программы возникает какая-то ошибка, то программа аварийно завершается. Иногда при этом она выдаёт сообщение об ошибке. Кажется, что это нормальная ситуация, но на самом деле большинство ошибок можно предусмотреть и научить программу правильно с ними работать. Для этого нам нужны обработчики ошибок. 

Что такое обработчик ошибок

Чтобы программа знала, что делать, если возникла какая-то ошибка, используют обработчики исключительных ситуаций, или, проще говоря, обработчики исключений. Смысл такой:

  1. Мы заранее прикидываем, в каком месте и почему может возникнуть ошибка.
  2. Пишем в этом месте специальный код, который предупредит компьютер, что это плановая ошибка и что у нас уже есть решение, мол, всё под контролем.
  3. Компьютер применяет наше решение и переходит к следующей команде.
  4. Программа не падает, не завершается с ошибкой, а продолжает работать.

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

Пример программы без обработчика исключений

Допустим, у нас в программе на Python предусмотрено чтение данных из файла и есть такой код:

file = open("myfile2.txt")

Но если на диске этого файла не будет, то компьютер, когда дойдёт до этой строчки, выдаст ошибку:

Uncaught SyntaxError: missing ) after argument list — что это значит

Давайте нарисуем это в виде простой схемы:

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

Программа с обработчиком исключений

Если мы знаем, что в каком-то месте возможна ошибка, то можем тогда предусмотреть этот сценарий и подстраховаться. Для этого используют обработчик и делают так:

  1. В том месте, где можно предусмотреть ошибку, делают специальный блок.
  2. В этом блоке запускают команду и смотрят, будет ошибка или нет.
  3. Если ошибки нет — программа работает дальше.
  4. Если возникла ошибка — выполнятся то, что написано в обработчике ошибок, а потом программа работает дальше.

В этой ситуации программа не зависнет и не вывалится с ошибкой, а сама сможет её обработать и делать дальше то, что нужно:

try:
    file = open("myfile2.txt")
except FileNotFoundError:
    print("Файл не найден, создаю новый")
    file = open("myfile2.txt","a")

Команда try — это начало нашего обработчика исключений. Она говорит компьютеру: «Попробуй выполнить вот эту команду, а мы посмотрим, что произойдёт». 

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

👉 Сравните текст этой ошибки с тем, что нам выдал компьютер в предыдущем разделе. 

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

Когда что-то не предусмотрено — будет ошибка

Если программе в этом блоке встретится другая ошибка, не та, которую мы предусмотрели, то программа остановится и всё перестанет работать. Например, вот какие ошибки могут возникнуть с файлом:

  • файл есть на диске, но к нему нет прав доступа;
  • файл занят другой программой;
  • сам диск повреждён и данные не читаются.

Во всех этих случаях программа сломается, потому что мы не предусмотрели эти ситуации:

Получается, всё нужно делать с обработкой исключений?

Нет, и вот почему:

  1. Обработка исключений занимает лишнее время, поэтому программа с ними работает медленнее, чем без них.
  2. Не всё можно предусмотреть. Если разработчик не знает, что здесь может быть ошибка, то и предусмотреть он это тоже не сможет.
  3. Конструкции с обработчиками делают код менее читаемым и понятным для человека. Ему нужно будет держать в голове точку начала обработки, понять, как обработка влияет на программу в целом, и выяснить, что будет с программой после работы обработчика ошибок.

Конечно, есть места в коде, которые лучше делать с обработкой ошибок: работа с файлами, сетевые запросы или получение внешних данных. Но запихивать исключения на каждую команду в программе точно не стоит.

Текст:

Михаил Полянин

Редактура:

Максим Ильяхов

Художник:

Даня Берковский

Корректор:

Ирина Михеева

Вёрстка:

Мария Дронова

Соцсети:

Олег Вешкурцев

Python 3: обработка исключений: try, except, finally, else

Смотреть материал на видео

На этом занятии мы с вами сделаем введение в очень важную тему – исключения. Помните, когда мы говорили о некоторых методах, например, удаление элемента из пустого списка:

a=[]; a.pop()

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

также приведет к ошибке ZeroDivisionError. Или такие операции:

int("12abc") #ValueError
"2"+5 #TypeError

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

Давайте вначале предположим, что у нас имеется вот такая программа для вычисления деления двух целых чисел:

x = input("x: ")
y = input("y: ")
x = int(x)
y = int(y)
 
res = x/y
print(res)

Если мы будем вводить целые числа, то она работает корректно, но если ввести в качестве игрека ноль или не числовую величину, то возникнут соответствующие исключения. Так вот, в Python есть механизм для отслеживания (отлавливания) таких моментов в работе программы. Для этого критический по выполнению код следует поместить в блок try:

try:
    x = int(x)
    y = int(y)
 
    res = x/y
except ZeroDivisionError:
    res = "деление на ноль"
print(res)

А после него записать except и название исключения, которое требуется отследить. В данном случае мы отлавливаем деление на ноль, т.е. исключение с именем ZeroDivisionError. Давайте запустим эту программу и посмотрим как она теперь будет работать. Вводим 1 и 0, программа теперь не завершается аварийно и в консоли видим сообщение «деление на ноль».

Более подробно блок try except работает так. Сначала идет выполнение программы внутри блока try. Если все проходит в штатном режиме, то выполнение доходит до блока except и он пропускается, не выполняется. И далее уже вызывается функция print и печатается полученный результат. Если же в процессе выполнения программы блока try возникает какое-либо исключение (любое), то выполнение программы прерывается и управление передается блоку except с соответствующим именем исключения. Если нужное имя в блоке except отсутствует, то исключение переходит на более высокий уровень (в данном случае к среде выполнения Python). И в случае отсутствия обработчика исключение считается

необработанным (unhandled exception) и программа завершается аварийно.

Чтобы отловить в блоке try несколько различных исключений, их можно указать в круглых скобках через запятую после ключевого слова except:

except (ZeroDivisionError, ValueError):
    res = "деление на ноль или нечисловое значение"

Или, для раздельной обработки, в разных блоках except:

except ZeroDivisionError:
    res = "деление на ноль"
except ValueError:
    res = "одно из введенных значений не число"

Если мы хотим при возникновении ошибок указывать служебное сообщение, записанное в соответствующих исключениях, то это делается так:

except ZeroDivisionError as z:
    res = z
except ValueError as v:
    res = v

То есть, после имени исключения (в действительности, это класс, но мы о них пока еще не говорили, поэтому будем воспринимать его просто как имя) ставится ключевое слово as и дальше переменная, которая будет ссылаться на класс ValueError, в котором хранится служебное сообщение о конкретной ошибке. Выводя его с помощью print, мы в консоли видим это сообщение.

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

else:
    print("Исключений не произошло")

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

Другим необязательным блоком является блок finally, который, наоборот, выполняется всегда после блока try, вне зависимости произошла ошибка или нет:

finally:
    print("Блок finally выполняется всегда")

Теперь, при запуске программы, мы всегда будем видеть это сообщение. И здесь часто возникает вопрос: зачем нужен этот блок, если он выполняется всегда после try? Мы с таким же успехом можем записать этот print сразу после этого блока и, вроде бы, все будет работать также? В действительности, нет.

Смотрите, если мы, например, уберем блок except с исключением ValueError, запустим программу и введем нечисловые значения, то, конечно, возникнет необработанное исключение, но при этом, блок finally все равно выполнился! Этого не произошло бы, если просто записать print после try.

Или, вот такой пример:

def getValues():
    x = input("x: ")
    y = input("y: ")
    try:
        x = int(x)
        y = int(y)
        return x,y
    except ValueError as v:
        print(v)
        return 0,0
    finally:
        print("finally выполняется до return")
 
x,y = getValues()
print(x,y)

Мы создаем функцию для ввода двух целых чисел и в блоке finally выводим сообщение. Этим сообщением мы покажем, что этот блок будет выполняться до операторов return, присутствующих в функции.

Наконец, мы можем прописывать except без указания имени класса исключения:

x = input("x: ")
y = input("y: ")
try:
    x = int(x)
    y = int(y)
 
    res = x/y
except:
    print("Произошло исключение")
else:
    print("Исключений не произошло")
finally:
    print("Блок finally выполняется всегда")
 
print(res)

В этом случае оператор print(«Произошло исключение») будет выполняться при любых ошибках в блоке try, но само исключение обработано не будет, т. е. программа завершится аварийно.

Вот так работает базовый механизм обработки исключений в Python. Конечно, мы здесь рассмотрели только отслеживание стандартных (встроенных) классов исключений. В действительности, можно добавлять свои и строить довольно гибкие программы по обработке исключительных ситуаций. Но для углубления в этот материал, нужно вначале изучить основы ООП в Python. Поэтому мы пока на этом остановимся, и в качестве заданий для самоподготовки попробуйте сделать следующие программы:

1. Напишите программу ввода натуральных чисел через запятую и преобразования этой строки в список целых чисел. (Используйте здесь функцию map для преобразования элементов последовательности строк в последовательность чисел). Реализовать обработку возможных исключений при таком преобразовании.

2. Написать функцию вычисления среднего арифметического элементов переданного ей списка. Реализовать обработку возможных исключений при ее работе.

3. Написать функцию-генератор (с использованием оператора yield) для удаления произвольного элемента из множества (с помощью метода pop()). Функция должна возвращать значение удаленного элемента. Реализовать обработку возможных исключений при ее работе.

Видео по теме

#1. Первое знакомство с Python Установка на компьютер

#2. Варианты исполнения команд. Переходим в PyCharm

#3. Переменные, оператор присваивания, функции type и id

#4. Числовые типы, арифметические операции

#5. Математические функции и работа с модулем math

#6. Функции print() и input(). Преобразование строк в числа int() и float()

#7. Логический тип bool. Операторы сравнения и операторы and, or, not

#8. Введение в строки. Базовые операции над строками

#9. Знакомство с индексами и срезами строк

#10. Основные методы строк

#11. Спецсимволы, экранирование символов, row-строки

#12. Форматирование строк: метод format и F-строки

#13. Списки — операторы и функции работы с ними

#14. Срезы списков и сравнение списков

#15. Основные методы списков

#16. Вложенные списки, многомерные списки

#17. Условный оператор if. Конструкция if-else

#18. Вложенные условия и множественный выбор. Конструкция if-elif-else

#19. Тернарный условный оператор. Вложенное тернарное условие

#20. Оператор цикла while

#21. Операторы циклов break, continue и else

#22. Оператор цикла for. Функция range()

#23. Примеры работы оператора цикла for. Функция enumerate()

#24. Итератор и итерируемые объекты. Функции iter() и next()

#25. Вложенные циклы. Примеры задач с вложенными циклами

#26. Треугольник Паскаля как пример работы вложенных циклов

#27. Генераторы списков (List comprehensions)

#28. Вложенные генераторы списков

#29. Введение в словари (dict). Базовые операции над словарями

#30. Методы словаря, перебор элементов словаря в цикле

#31. Кортежи (tuple) и их методы

#32. Множества (set) и их методы

#33. Операции над множествами, сравнение множеств

#34. Генераторы множеств и генераторы словарей

#35. Функции: первое знакомство, определение def и их вызов

#36. Оператор return в функциях. Функциональное программирование

#37. Алгоритм Евклида для нахождения НОД

#38. Именованные аргументы. Фактические и формальные параметры

#39. Функции с произвольным числом параметров *args и **kwargs

#40. Операторы * и ** для упаковки и распаковки коллекций

#41. Рекурсивные функции

#42. Анонимные (lambda) функции

#43. Области видимости переменных. Ключевые слова global и nonlocal

#44. Замыкания в Python

#45. Введение в декораторы функций

#46. Декораторы с параметрами. Сохранение свойств декорируемых функций

#47. Импорт стандартных модулей. Команды import и from

#48. Импорт собственных модулей

#49. Установка сторонних модулей (pip install). Пакетная установка

#50. Пакеты (package) в Python. Вложенные пакеты

#51. Функция open. Чтение данных из файла

#52. Исключение FileNotFoundError и менеджер контекста (with) для файлов

#53. Запись данных в файл в текстовом и бинарном режимах

#54. Выражения генераторы

#55. Функция-генератор. Оператор yield

#56. Функция map. Примеры ее использования

#57. Функция filter для отбора значений итерируемых объектов

#58. Функция zip. Примеры использования

#59. Сортировка с помощью метода sort и функции sorted

#60. Аргумент key для сортировки коллекций по ключу

#61. Функции isinstance и type для проверки типов данных

#62. Функции all и any. Примеры их использования

#63. Расширенное представление чисел. Системы счисления

#64. Битовые операции И, ИЛИ, НЕ, XOR. Сдвиговые операторы

#65. Модуль random стандартной библиотеки

Что такое обработка исключений? — SearchSoftwareQuality

Качество программного обеспечения

К

  • Александр С. Гиллис, Технический писатель и редактор

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

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

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

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

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

Как используется обработка исключений?

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

Блок try обнаруживает и отправляет любые найденные исключения в блоки catch, которые затем их обрабатывают.

Обработка исключений может перехватывать и генерировать исключений. Если функция обнаружения в блоке кода не может справиться с аномалией, исключение передается функции, которая может обработать исключение. Оператор catch — это группа операторов, которые обрабатывают конкретное выброшенное исключение. Параметры перехвата определяют тип создаваемого исключения.

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

Код обработки ошибок также можно отделить от обычного кода с помощью блоков попыток , которые представляют собой код, заключенный в фигурные скобки или квадратные скобки, которые могут вызвать исключение. Пробные блоки могут помочь программистам классифицировать объекты исключений.

Скобка try содержит код, который обнаруживает исключение и предотвращает сбой приложения.

Какие существуют типы исключений?

Исключения могут относиться к следующим двум классам исключений:

  1. Проверенные исключения. Также называемые исключениями времени компиляции , компилятор проверяет эти исключения в процессе компиляции, чтобы убедиться, что исключение обрабатывается программистом. Если нет, то в системе отображается ошибка компиляции. К проверенным исключениям относятся SQLException и ClassNotFoundException.
  2. Непроверенные исключения. Также называемые исключениями времени выполнения , эти исключения возникают во время выполнения программы. Эти исключения не проверяются во время компиляции, поэтому за обработку этих исключений отвечает программист. Непроверенные исключения не дают ошибок компиляции. Примеры непроверенных исключений включают NullPointerException и IllegalArgumentException.