Обработка ошибок питон

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

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

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

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

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

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

Ошибки могут быть разных видов:

  • Синтаксические
  • Недостаточно памяти
  • Ошибки рекурсии
  • Исключения

Разберем их по очереди.

Синтаксические ошибки (SyntaxError)

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

Рассмотрим на примере.

a = 8
b = 10
c = a b
File "", line 3
 c = a b
       ^
SyntaxError: invalid syntax

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

Недостаточно памяти (OutofMemoryError)

Ошибки памяти чаще всего связаны с оперативной памятью компьютера и относятся к структуре данных под названием “Куча” (heap). Если есть крупные объекты (или) ссылки на подобные, то с большой долей вероятности возникнет ошибка OutofMemory. Она может появиться по нескольким причинам:

  • Использование 32-битной архитектуры Python (максимальный объем выделенной памяти невысокий, между 2 и 4 ГБ);
  • Загрузка файла большого размера;
  • Запуск модели машинного обучения/глубокого обучения и много другое;

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

Но поскольку Python использует архитектуру управления памятью из языка C (функция malloc()), не факт, что все процессы восстановятся — в некоторых случаях MemoryError приведет к остановке. Следовательно, обрабатывать такие ошибки не рекомендуется, и это не считается хорошей практикой.

Ошибка рекурсии (RecursionError)

Эта ошибка связана со стеком и происходит при вызове функций. Как и предполагает название, ошибка рекурсии возникает, когда внутри друг друга исполняется много методов (один из которых — с бесконечной рекурсией), но это ограничено размером стека.

Все локальные переменные и методы размещаются в стеке. Для каждого вызова метода создается стековый кадр (фрейм), внутрь которого помещаются данные переменной или результат вызова метода. Когда исполнение метода завершается, его элемент удаляется.

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

def recursion():
    return recursion()

recursion()
---------------------------------------------------------------------------

RecursionError                            Traceback (most recent call last)

 in 
----> 1 recursion()


 in recursion()
      1 def recursion():
----> 2     return recursion()


... last 1 frames repeated, from the frame below ...


 in recursion()
      1 def recursion():
----> 2     return recursion()


RecursionError: maximum recursion depth exceeded

Ошибка отступа (IndentationError)

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

Пример:

for i in range(10):
    print('Привет Мир!')
  File "", line 2
    print('Привет Мир!')
        ^
IndentationError: expected an indented block

Исключения

Даже если синтаксис в инструкции или само выражение верны, они все равно могут вызывать ошибки при исполнении. Исключения Python — это ошибки, обнаруживаемые при исполнении, но не являющиеся критическими. Скоро вы узнаете, как справляться с ними в программах Python. Объект исключения создается при вызове исключения Python. Если скрипт не обрабатывает исключение явно, программа будет остановлена принудительно.

Программы обычно не обрабатывают исключения, что приводит к подобным сообщениям об ошибке:

Ошибка типа (TypeError)

a = 2
b = 'PythonRu'
a + b
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

 in 
      1 a = 2
      2 b = 'PythonRu'
----> 3 a + b


TypeError: unsupported operand type(s) for +: 'int' and 'str'

Ошибка деления на ноль (ZeroDivisionError)

10 / 0
---------------------------------------------------------------------------

ZeroDivisionError                         Traceback (most recent call last)

 in 
----> 1 10 / 0


ZeroDivisionError: division by zero

Есть разные типы исключений в Python и их тип выводится в сообщении: вверху примеры TypeError и ZeroDivisionError. Обе строки в сообщениях об ошибке представляют собой имена встроенных исключений Python.

Оставшаяся часть строки с ошибкой предлагает подробности о причине ошибки на основе ее типа.

Теперь рассмотрим встроенные исключения Python.

Встроенные исключения

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
      |    +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

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

  • Try: он запускает блок кода, в котором ожидается ошибка.
  • Except: здесь определяется тип исключения, который ожидается в блоке try (встроенный или созданный).
  • Else: если исключений нет, тогда исполняется этот блок (его можно воспринимать как средство для запуска кода в том случае, если ожидается, что часть кода приведет к исключению).
  • Finally: вне зависимости от того, будет ли исключение или нет, этот блок кода исполняется всегда.

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

Ошибка прерывания с клавиатуры (KeyboardInterrupt)

Исключение KeyboardInterrupt вызывается при попытке остановить программу с помощью сочетания Ctrl + C или Ctrl + Z в командной строке или ядре в Jupyter Notebook. Иногда это происходит неумышленно и подобная обработка поможет избежать подобных ситуаций.

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

try:
    inp = input()
    print('Нажмите Ctrl+C и прервите Kernel:')
except KeyboardInterrupt:
    print('Исключение KeyboardInterrupt')
else:
    print('Исключений не произошло')

Исключение KeyboardInterrupt

Стандартные ошибки (StandardError)

Рассмотрим некоторые базовые ошибки в программировании.

Арифметические ошибки (ArithmeticError)

  • Ошибка деления на ноль (Zero Division);
  • Ошибка переполнения (OverFlow);
  • Ошибка плавающей точки (Floating Point);

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

Деление на ноль (ZeroDivisionError)

Когда делитель (второй аргумент операции деления) или знаменатель равны нулю, тогда результатом будет ошибка деления на ноль.

try:  
    a = 100 / 0
    print(a)
except ZeroDivisionError:  
    print("Исключение ZeroDivisionError." )
else:  
    print("Успех, нет ошибок!")
Исключение ZeroDivisionError.

Переполнение (OverflowError)

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

try:  
    import math
    print(math.exp(1000))
except OverflowError:  
    print("Исключение OverFlow.")
else:  
    print("Успех, нет ошибок!")
Исключение OverFlow.

Ошибка утверждения (AssertionError)

Когда инструкция утверждения не верна, вызывается ошибка утверждения.

Рассмотрим пример. Предположим, есть две переменные: a и b. Их нужно сравнить. Чтобы проверить, равны ли они, необходимо использовать ключевое слово assert, что приведет к вызову исключения Assertion в том случае, если выражение будет ложным.

try:  
    a = 100
    b = "PythonRu"
    assert a == b
except AssertionError:  
    print("Исключение AssertionError.")
else:  
    print("Успех, нет ошибок!")

Исключение AssertionError.

Ошибка атрибута (AttributeError)

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

class Attributes(obj):
    a = 2
    print(a)

try:
    obj = Attributes()
    print(obj.attribute)
except AttributeError:
    print("Исключение AttributeError.")

2
Исключение AttributeError.

Ошибка импорта (ModuleNotFoundError)

Ошибка импорта вызывается при попытке импортировать несуществующий (или неспособный загрузиться) модуль в стандартном пути или даже при допущенной ошибке в имени.

import nibabel
---------------------------------------------------------------------------

ModuleNotFoundError                       Traceback (most recent call last)

 in 
----> 1 import nibabel


ModuleNotFoundError: No module named 'nibabel'

Ошибка поиска (LookupError)

LockupError выступает базовым классом для исключений, которые происходят, когда key или index используются для связывания или последовательность списка/словаря неверна или не существует.

Здесь есть два вида исключений:

  • Ошибка индекса (IndexError);
  • Ошибка ключа (KeyError);

Ошибка ключа

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

try:  
    a = {1:'a', 2:'b', 3:'c'}  
    print(a[4])  
except LookupError:  
    print("Исключение KeyError.")
else:  
    print("Успех, нет ошибок!")

Исключение KeyError.

Ошибка индекса

Если пытаться получить доступ к индексу (последовательности) списка, которого не существует в этом списке или находится вне его диапазона, будет вызвана ошибка индекса (IndexError: list index out of range python).

try:
    a = ['a', 'b', 'c']  
    print(a[4])  
except LookupError:  
    print("Исключение IndexError, индекс списка вне диапазона.")
else:  
    print("Успех, нет ошибок!")
Исключение IndexError, индекс списка вне диапазона.

Ошибка памяти (MemoryError)

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

Ошибка имени (NameError)

Ошибка имени возникает, когда локальное или глобальное имя не находится.

В следующем примере переменная ans не определена. Результатом будет ошибка NameError.

try:
    print(ans)
except NameError:  
    print("NameError: переменная 'ans' не определена")
else:  
    print("Успех, нет ошибок!")
NameError: переменная 'ans' не определена

Ошибка выполнения (Runtime Error)

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

class BaseClass(object):
    """Опередляем класс"""
    def __init__(self):
        super(BaseClass, self).__init__()
    def do_something(self):
	# функция ничего не делает
        raise NotImplementedError(self.__class__.__name__ + '.do_something')

class SubClass(BaseClass):
    """Реализует функцию"""
    def do_something(self):
        # действительно что-то делает
        print(self.__class__.__name__ + ' что-то делает!')

SubClass().do_something()
BaseClass().do_something()

SubClass что-то делает!



---------------------------------------------------------------------------

NotImplementedError                       Traceback (most recent call last)

 in 
     14
     15 SubClass().do_something()
---> 16 BaseClass().do_something()


 in do_something(self)
      5     def do_something(self):
      6         # функция ничего не делает
----> 7         raise NotImplementedError(self.__class__.__name__ + '.do_something')
      8
      9 class SubClass(BaseClass):


NotImplementedError: BaseClass.do_something

Ошибка типа (TypeError)

Ошибка типа вызывается при попытке объединить два несовместимых операнда или объекта.

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

try:
    a = 5
    b = "PythonRu"
    c = a + b
except TypeError:
    print('Исключение TypeError')
else:
    print('Успех, нет ошибок!')

Исключение TypeError

Ошибка значения (ValueError)

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

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

try:
    print(float('PythonRu'))
except ValueError:
    print('ValueError: не удалось преобразовать строку в float: 'PythonRu'')
else:
    print('Успех, нет ошибок!')
ValueError: не удалось преобразовать строку в float: 'PythonRu'

Пользовательские исключения в Python

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

Это можно сделать, создав новый класс, который будет наследовать из класса Exception в Python.

class UnAcceptedValueError(Exception):   
    def __init__(self, data):    
        self.data = data
    def __str__(self):
        return repr(self.data)

Total_Marks = int(input("Введите общее количество баллов: "))
try:
    Num_of_Sections = int(input("Введите количество разделов: "))
    if(Num_of_Sections < 1):
        raise UnAcceptedValueError("Количество секций не может быть меньше 1")
except UnAcceptedValueError as e:
    print("Полученная ошибка:", e.data)

Введите общее количество баллов: 10
Введите количество разделов: 0
Полученная ошибка: Количество секций не может быть меньше 1

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

Недостатки обработки исключений в Python

У использования исключений есть свои побочные эффекты, как, например, то, что программы с блоками try-except работают медленнее, а количество кода возрастает.

Дальше пример, где модуль Python timeit используется для проверки времени исполнения 2 разных инструкций. В stmt1 для обработки ZeroDivisionError используется try-except, а в stmt2if. Затем они выполняются 10000 раз с переменной a=0. Суть в том, чтобы показать разницу во времени исполнения инструкций. Так, stmt1 с обработкой исключений занимает больше времени чем stmt2, который просто проверяет значение и не делает ничего, если условие не выполнено.

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

import timeit
setup="a=0"
stmt1 = '''
try:
    b=10/a
except ZeroDivisionError:
    pass'''

stmt2 = '''
if a!=0:
    b=10/a'''

print("time=",timeit.timeit(stmt1,setup,number=10000))
print("time=",timeit.timeit(stmt2,setup,number=10000))

time= 0.003897680000136461
time= 0.0002797570000439009

Выводы!

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

Обработка исключений — один из основных факторов, который делает код готовым к развертыванию. Это простая концепция, построенная всего на 4 блоках: try выискивает исключения, а except их обрабатывает.

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

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

Напишем программу, которая будет считать обратные значения для целых чисел из заданного диапазона и выводить их в одну строку с разделителем «;». Один из вариантов кода для решения этой задачи выглядит так:

print(";".join(str(1 / x) for x in range(int(input()), int(input()) + 1)))

Программа получилась в одну строчку за счёт использования списочных выражений. Однако при вводе диапазона чисел, включающем в себя 0 (например, от -1 до 1), программа выдаст следующую ошибку:

ZeroDivisionError: division by zero

В программе произошла ошибка «деление на ноль». Такая ошибка, возникающая при выполнении программы и останавливающая её работу, называется исключением.

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

interval = range(int(input()), int(input()) + 1)
if 0 in interval:
    print("Диапазон чисел содержит 0.")
else:
    print(";".join(str(1 / x) for x in interval))

Теперь для диапазона, включающего в себя 0, например, от -2 до 2, исключения ZeroDivisionError не возникнет. Однако при вводе строки, которую невозможно преобразовать в целое число (например, «a»), будет вызвано другое исключение:

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

Произошло исключение ValueError. Для борьбы с этой ошибкой нам придётся проверить, что строка состоит только из цифр. Сделать это нужно до преобразования в число. Тогда наша программа будет выглядеть так:

start = input()
end = input()
# Метод lstrip("-"), удаляющий символы "-" в начале строки, нужен для учёта
# отрицательных чисел, иначе isdigit() вернёт для них False
if not (start.lstrip("-").isdigit() and end.lstrip("-").isdigit()):
    print("Необходимо ввести два числа.")
else:
    interval = range(int(start), int(end) + 1)
    if 0 in interval:
        print("Диапазон чисел содержит 0.")
    else:
        print(";".join(str(1 / x) for x in interval))

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

Подход, который был нами применён для предотвращения ошибок, называется «Look Before You Leap» (LBYL), или «посмотри перед прыжком». В программе, реализующей такой подход, проверяются возможные условия возникновения ошибок до исполнения основного кода.

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

Существует другой подход для работы с ошибками: «Easier to Ask Forgiveness than Permission» (EAFP) или «проще извиниться, чем спрашивать разрешение». В этом подходе сначала исполняется код, а в случае возникновения ошибок происходит их обработка. Подход EAFP реализован в Python в виде обработки исключений.

Исключения в Python являются классами ошибок. В Python есть много стандартных исключений. Они имеют определённую иерархию за счёт механизма наследования классов. В документации Python версии 3.10.8 приводится следующее дерево иерархии стандартных исключений:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
      |    +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- EncodingWarning
           +-- ResourceWarning

Для обработки исключения в Python используется следующий синтаксис:

try:
    <код , который может вызвать исключения при выполнении>
except <классисключения_1>:
    <код обработки исключения>
except <классисключения_2>:
    <код обработки исключения>
...
else:
    <код выполняется, если не вызвано исключение в блоке try>
finally:
    <код , который выполняется всегда>

Блок try содержит код, в котором нужно обработать исключения, если они возникнут. При возникновении исключения интерпретатор последовательно проверяет в каком из блоков except обрабатывается это исключение. Исключение обрабатывается в первом блоке except, обрабатывающем класс этого исключения или базовый класс возникшего исключения. Необходимо учитывать иерархию исключений для определения порядка их обработки в блоках except. Начинать обработку исключений следует с более узких классов исключений. Если начать с более широкого класса исключения, например, Exception, то всегда при возникновении исключения будет срабатывать первый блок except. Сравните два следующих примера. В первом порядок обработки исключений указан от производных классов к базовым, а во втором – наоборот.

try:
    print(1 / int(input()))
except ZeroDivisionError:
    print("Ошибка деления на ноль.")
except ValueError:
    print("Невозможно преобразовать строку в число.")
except Exception:
    print("Неизвестная ошибка.")

При вводе значений «0» и «a» получим ожидаемый соответствующий возникающим исключениям вывод:

Невозможно преобразовать строку в число.

и

Ошибка деления на ноль.

Второй пример:

try:
    print(1 / int(input()))
except Exception:
    print("Неизвестная ошибка.")
except ZeroDivisionError:
    print("Ошибка деления на ноль.")
except ValueError:
    print("Невозможно преобразовать строку в число.")

При вводе значений «0» и «a» получим в обоих случаях неинформативный вывод:

Неизвестная ошибка.

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

try:
    print(1 / int(input()))
except ZeroDivisionError:
    print("Ошибка деления на ноль.")
except ValueError:
    print("Невозможно преобразовать строку в число.")
except Exception:
    print("Неизвестная ошибка.")
else:
    print("Операция выполнена успешно.")

Теперь при вводе корректного значения, например, «5», вывод программы будет следующим:

2.0
Операция выполнена успешно.

Блок finally выполняется всегда, даже если возникло какое-то исключение, не учтённое в блоках except или код в этих блоках сам вызвал какое-либо исключение. Добавим в нашу программу вывод строки «Программа завершена» в конце программы даже при возникновении исключений:

try:
    print(1 / int(input()))
except ZeroDivisionError:
    print("Ошибка деления на ноль.")
except ValueError:
    print("Невозможно преобразовать строку в число.")
except Exception:
    print("Неизвестная ошибка.")
else:
    print("Операция выполнена успешно.")
finally:
    print("Программа завершена.")

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

try:
    print(";".join(str(1 / x) for x in range(int(input()), int(input()) + 1)))
except ZeroDivisionError:
    print("Диапазон чисел содержит 0.")
except ValueError:
    print("Необходимо ввести два числа.")

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

Исключения можно принудительно вызывать с помощью оператора raise. Этот оператор имеет следующий синтаксис:

raise <класс исключения>(параметры)

В качестве параметра можно, например, передать строку с сообщением об ошибке.

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

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

  • NumbersError – базовый класс исключения;
  • EvenError – исключение, которое вызывается при наличии хотя бы одного чётного числа;
  • NegativeError – исключение, которое вызывается при наличии хотя бы одного отрицательного числа.
class NumbersError(Exception):
    pass


class EvenError(NumbersError):
    pass


class NegativeError(NumbersError):
    pass


def no_even(numbers):
    if all(x % 2 != 0 for x in numbers):
        return True
    raise EvenError("В списке не должно быть чётных чисел")


def no_negative(numbers):
    if all(x >= 0 for x in numbers):
        return True
    raise NegativeError("В списке не должно быть отрицательных чисел")


def main():
    print("Введите числа в одну строку через пробел:")
    try:
        numbers = [int(x) for x in input().split()]
        if no_negative(numbers) and no_even(numbers):
            print(f"Сумма чисел равна: {sum(numbers)}.")
    except NumbersError as e:  # обращение к исключению как к объекту
        print(f"Произошла ошибка: {e}.")
    except Exception as e:
        print(f"Произошла непредвиденная ошибка: {e}.")

        
if __name__ == "__main__":
    main()

Обратите внимание: в программе основной код выделен в функцию main. А код вне функций содержит только условный оператор и вызов функции main при выполнении условия __name__ == "__main__". Это условие проверяет, запущен ли файл как самостоятельная программа или импортирован как модуль.

Любая программа, написанная на языке программирования Python может быть импортирована как модуль в другую программу. В идеологии Python импортировать модуль – значит полностью его выполнить. Если основной код модуля содержит вызовы функций, ввод или вывод данных без использования указанного условия __name__ == "__main__", то произойдёт полноценный запуск программы. А это не всегда удобно, если из модуля нужна только отдельная функция или какой-либо класс.

При изучении модуля itertools, мы говорили о том, как импортировать модуль в программу. Покажем ещё раз два способа импорта на примере собственного модуля.

Для импорта модуля из файла, например example_module.py, нужно указать его имя, если он находится в той же папке, что и импортирующая его программа:

import example_module

Если требуется отдельный компонент модуля, например функция или класс, то импорт можно осуществить так:

from example_module import some_function, ExampleClass

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

Содержание:развернуть

  • Как устроен механизм исключений
  • Как обрабатывать исключения в Python (try except)
  • As — сохраняет ошибку в переменную

  • Finally — выполняется всегда

  • Else — выполняется когда исключение не было вызвано

  • Несколько блоков except

  • Несколько типов исключений в одном блоке except

  • Raise — самостоятельный вызов исключений

  • Как пропустить ошибку

  • Исключения в lambda функциях
  • 20 типов встроенных исключений в Python
  • Как создать свой тип Exception

Программа, написанная на языке Python, останавливается сразу как обнаружит ошибку. Ошибки могут быть (как минимум) двух типов:

  • Синтаксические ошибки — возникают, когда написанное выражение не соответствует правилам языка (например, написана лишняя скобка);
  • Исключения — возникают во время выполнения программы (например, при делении на ноль).

Синтаксические ошибки исправить просто (если вы используете IDE, он их подсветит). А вот с исключениями всё немного сложнее — не всегда при написании программы можно сказать возникнет или нет в данном месте исключение. Чтобы приложение продолжило работу при возникновении проблем, такие ошибки нужно перехватывать и обрабатывать с помощью блока try/except.

Как устроен механизм исключений

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

💁‍♂️ Пример: напишем скрипт, в котором функция ожидает число, а мы передаём сроку (это вызовет исключение «TypeError»):

def b(value):
print("-> b")
print(value + 1) # ошибка тут

def a(value):
print("-> a")
b(value)

a("10")

> -> a
> -> b
> Traceback (most recent call last):
> File "test.py", line 11, in <module>
> a("10")
> File "test.py", line 8, in a
> b(value)
> File "test.py", line 3, in b
> print(value + 1)
> TypeError: can only concatenate str (not "int") to str

В данном примере мы запускаем файл «test.py» (через консоль). Вызывается функция «a«, внутри которой вызывается функция «b«. Все работает хорошо до сточки print(value + 1). Тут интерпретатор понимает, что нельзя конкатенировать строку с числом, останавливает выполнение программы и вызывает исключение «TypeError».

Далее ошибка передается по цепочке в обратном направлении: «b» → «a» → «test.py«. Так как в данном примере мы не позаботились обработать эту ошибку, вся информация по ошибке отобразится в консоли в виде Traceback.

Traceback (трассировка) — это отчёт, содержащий вызовы функций, выполненные в определенный момент. Трассировка помогает узнать, что пошло не так и в каком месте это произошло.

Traceback лучше читать снизу вверх ↑

Пример Traceback в Python

В нашем примере Traceback содержится следующую информацию (читаем снизу вверх):

  1. TypeError — тип ошибки (означает, что операция не может быть выполнена с переменной этого типа);
  2. can only concatenate str (not "int") to str — подробное описание ошибки (конкатенировать можно только строку со строкой);
  3. Стек вызова функций (1-я линия — место, 2-я линия — код). В нашем примере видно, что в файле «test.py» на 11-й линии был вызов функции «a» со строковым аргументом «10». Далее был вызов функции «b». print(value + 1) это последнее, что было выполнено — тут и произошла ошибка.
  4. most recent call last — означает, что самый последний вызов будет отображаться последним в стеке (в нашем примере последним выполнился print(value + 1)).

В Python ошибку можно перехватить, обработать, и продолжить выполнение программы — для этого используется конструкция try ... except ....

Как обрабатывать исключения в Python (try except)

В Python исключения обрабатываются с помощью блоков try/except. Для этого операция, которая может вызвать исключение, помещается внутрь блока try. А код, который должен быть выполнен при возникновении ошибки, находится внутри except.

Например, вот как можно обработать ошибку деления на ноль:

try:
a = 7 / 0
except:
print('Ошибка! Деление на 0')

Здесь в блоке try находится код a = 7 / 0 — при попытке его выполнить возникнет исключение и выполнится код в блоке except (то есть будет выведено сообщение «Ошибка! Деление на 0»). После этого программа продолжит свое выполнение.

💭 PEP 8 рекомендует, по возможности, указывать конкретный тип исключения после ключевого слова except (чтобы перехватывать и обрабатывать конкретные исключения):

try:
a = 7 / 0
except ZeroDivisionError:
print('Ошибка! Деление на 0')

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

try:
a = 7 / 0
except Exception:
print('Любая ошибка!')

As — сохраняет ошибку в переменную

Перехваченная ошибка представляет собой объект класса, унаследованного от «BaseException». С помощью ключевого слова as можно записать этот объект в переменную, чтобы обратиться к нему внутри блока except:

try:
file = open('ok123.txt', 'r')
except FileNotFoundError as e:
print(e)

> [Errno 2] No such file or directory: 'ok123.txt'

В примере выше мы обращаемся к объекту класса «FileNotFoundError» (при выводе на экран через print отобразится строка с полным описанием ошибки).

У каждого объекта есть поля, к которым можно обращаться (например если нужно логировать ошибку в собственном формате):

import datetime

now = datetime.datetime.now().strftime("%d-%m-%Y %H:%M:%S")

try:
file = open('ok123.txt', 'r')
except FileNotFoundError as e:
print(f"{now} [FileNotFoundError]: {e.strerror}, filename: {e.filename}")

> 20-11-2021 18:42:01 [FileNotFoundError]: No such file or directory, filename: ok123.txt

Finally — выполняется всегда

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

Обычно try/except используется для перехвата исключений и восстановления нормальной работы приложения, а try/finally для того, чтобы гарантировать выполнение определенных действий (например, для закрытия внешних ресурсов, таких как ранее открытые файлы).

В следующем примере откроем файл и обратимся к несуществующей строке:

file = open('ok.txt', 'r')

try:
lines = file.readlines()
print(lines[5])
finally:
file.close()
if file.closed:
print("файл закрыт!")

> файл закрыт!
> Traceback (most recent call last):
> File "test.py", line 5, in <module>
> print(lines[5])
> IndexError: list index out of range

Даже после исключения «IndexError», сработал код в секции finally, который закрыл файл.

p.s. данный пример создан для демонстрации, в реальном проекте для работы с файлами лучше использовать менеджер контекста with.

Также можно использовать одновременно три блока try/except/finally. В этом случае:

  • в try — код, который может вызвать исключения;
  • в except — код, который должен выполниться при возникновении исключения;
  • в finally — код, который должен выполниться в любом случае.

def sum(a, b):
res = 0

try:
res = a + b
except TypeError:
res = int(a) + int(b)
finally:
print(f"a = {a}, b = {b}, res = {res}")

sum(1, "2")

> a = 1, b = 2, res = 3

Else — выполняется когда исключение не было вызвано

Иногда нужно выполнить определенные действия, когда код внутри блока try не вызвал исключения. Для этого используется блок else.

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

b = int(input('b = '))
c = int(input('c = '))
try:
a = b / c
except ZeroDivisionError:
print('Ошибка! Деление на 0')
else:
print(f"a = {a}")

> b = 10
> c = 1
> a = 10.0

В этом случае, если пользователь присвоит переменной «с» ноль, то появится исключение и будет выведено сообщение «‘Ошибка! Деление на 0′», а код внутри блока else выполняться не будет. Если ошибки не будет, то на экране появятся результаты деления.

Несколько блоков except

В программе может возникнуть несколько исключений, например:

  1. Ошибка преобразования введенных значений к типу float («ValueError»);
  2. Деление на ноль («ZeroDivisionError»).

В Python, чтобы по-разному обрабатывать разные типы ошибок, создают несколько блоков except:

try:
b = float(input('b = '))
c = float(input('c = '))
a = b / c
except ZeroDivisionError:
print('Ошибка! Деление на 0')
except ValueError:
print('Число введено неверно')
else:
print(f"a = {a}")

> b = 10
> c = 0
> Ошибка! Деление на 0

> b = 10
> c = питон
> Число введено неверно

Теперь для разных типов ошибок есть свой обработчик.

Несколько типов исключений в одном блоке except

Можно также обрабатывать в одном блоке except сразу несколько исключений. Для этого они записываются в круглых скобках, через запятую сразу после ключевого слова except. Чтобы обработать сообщения «ZeroDivisionError» и «ValueError» в одном блоке записываем их следующим образом:

try:
b = float(input('b = '))
c = float(input('c = '))
a = b / c
except (ZeroDivisionError, ValueError) as er:
print(er)
else:
print('a = ', a)

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

Raise — самостоятельный вызов исключений

Исключения можно генерировать самостоятельно — для этого нужно запустить оператор raise.

min = 100
if min > 10:
raise Exception('min must be less than 10')

> Traceback (most recent call last):
> File "test.py", line 3, in <module>
> raise Exception('min value must be less than 10')
> Exception: min must be less than 10

Перехватываются такие сообщения точно так же, как и остальные:

min = 100

try:
if min > 10:
raise Exception('min must be less than 10')
except Exception:
print('Моя ошибка')

> Моя ошибка

Кроме того, ошибку можно обработать в блоке except и пробросить дальше (вверх по стеку) с помощью raise:

min = 100

try:
if min > 10:
raise Exception('min must be less than 10')
except Exception:
print('Моя ошибка')
raise

> Моя ошибка
> Traceback (most recent call last):
> File "test.py", line 5, in <module>
> raise Exception('min must be less than 10')
> Exception: min must be less than 10

Как пропустить ошибку

Иногда ошибку обрабатывать не нужно. В этом случае ее можно пропустить с помощью pass:

try:
a = 7 / 0
except ZeroDivisionError:
pass

Исключения в lambda функциях

Обрабатывать исключения внутри lambda функций нельзя (так как lambda записывается в виде одного выражения). В этом случае нужно использовать именованную функцию.

20 типов встроенных исключений в Python

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

BaseException
SystemExit
KeyboardInterrupt
GeneratorExit
Exception
ArithmeticError
AssertionError
...
...
...
ValueError
Warning

Все исключения в Python наследуются от базового BaseException:

  • SystemExit — системное исключение, вызываемое функцией sys.exit() во время выхода из приложения;
  • KeyboardInterrupt — возникает при завершении программы пользователем (чаще всего при нажатии клавиш Ctrl+C);
  • GeneratorExit — вызывается методом close объекта generator;
  • Exception — исключения, которые можно и нужно обрабатывать (предыдущие были системными и их трогать не рекомендуется).

От Exception наследуются:

1 StopIteration — вызывается функцией next в том случае если в итераторе закончились элементы;

2 ArithmeticError — ошибки, возникающие при вычислении, бывают следующие типы:

  • FloatingPointError — ошибки при выполнении вычислений с плавающей точкой (встречаются редко);
  • OverflowError — результат вычислений большой для текущего представления (не появляется при операциях с целыми числами, но может появиться в некоторых других случаях);
  • ZeroDivisionError — возникает при попытке деления на ноль.

3 AssertionError — выражение, используемое в функции assert неверно;

4 AttributeError — у объекта отсутствует нужный атрибут;

5 BufferError — операция, для выполнения которой требуется буфер, не выполнена;

6 EOFError — ошибка чтения из файла;

7 ImportError — ошибка импортирования модуля;

8 LookupError — неверный индекс, делится на два типа:

  • IndexError — индекс выходит за пределы диапазона элементов;
  • KeyError — индекс отсутствует (для словарей, множеств и подобных объектов);

9 MemoryError — память переполнена;

10 NameError — отсутствует переменная с данным именем;

11 OSError — исключения, генерируемые операционной системой:

  • ChildProcessError — ошибки, связанные с выполнением дочернего процесса;
  • ConnectionError — исключения связанные с подключениями (BrokenPipeError, ConnectionResetError, ConnectionRefusedError, ConnectionAbortedError);
  • FileExistsError — возникает при попытке создания уже существующего файла или директории;
  • FileNotFoundError — генерируется при попытке обращения к несуществующему файлу;
  • InterruptedError — возникает в том случае если системный вызов был прерван внешним сигналом;
  • IsADirectoryError — программа обращается к файлу, а это директория;
  • NotADirectoryError — приложение обращается к директории, а это файл;
  • PermissionError — прав доступа недостаточно для выполнения операции;
  • ProcessLookupError — процесс, к которому обращается приложение не запущен или отсутствует;
  • TimeoutError — время ожидания истекло;

12 ReferenceError — попытка доступа к объекту с помощью слабой ссылки, когда объект не существует;

13 RuntimeError — генерируется в случае, когда исключение не может быть классифицировано или не подпадает под любую другую категорию;

14 NotImplementedError — абстрактные методы класса нуждаются в переопределении;

15 SyntaxError — ошибка синтаксиса;

16 SystemError — сигнализирует о внутренне ошибке;

17 TypeError — операция не может быть выполнена с переменной этого типа;

18 ValueError — возникает когда в функцию передается объект правильного типа, но имеющий некорректное значение;

19 UnicodeError — исключение связанное с кодирование текста в unicode, бывает трех видов:

  • UnicodeEncodeError — ошибка кодирования;
  • UnicodeDecodeError — ошибка декодирования;
  • UnicodeTranslateError — ошибка перевода unicode.

20 Warning — предупреждение, некритическая ошибка.

💭 Посмотреть всю цепочку наследования конкретного типа исключения можно с помощью модуля inspect:

import inspect

print(inspect.getmro(TimeoutError))

> (<class 'TimeoutError'>, <class 'OSError'>, <class 'Exception'>, <class 'BaseException'>, <class 'object'>)

📄 Подробное описание всех классов встроенных исключений в Python смотрите в официальной документации.

Как создать свой тип Exception

В Python можно создавать свои исключения. При этом есть одно обязательное условие: они должны быть потомками класса Exception:

class MyError(Exception):
def __init__(self, text):
self.txt = text

try:
raise MyError('Моя ошибка')
except MyError as er:
print(er)

> Моя ошибка


С помощью try/except контролируются и обрабатываются ошибки в приложении. Это особенно актуально для критически важных частей программы, где любые «падения» недопустимы (или могут привести к негативным последствиям). Например, если программа работает как «демон», падение приведет к полной остановке её работы. Или, например, при временном сбое соединения с базой данных, программа также прервёт своё выполнение (хотя можно было отловить ошибку и попробовать соединиться в БД заново).

Вместе с try/except можно использовать дополнительные блоки. Если использовать все блоки описанные в статье, то код будет выглядеть так:

try:
# попробуем что-то сделать
except (ZeroDivisionError, ValueError) as e:
# обрабатываем исключения типа ZeroDivisionError или ValueError
except Exception as e:
# исключение не ZeroDivisionError и не ValueError
# поэтому обрабатываем исключение общего типа (унаследованное от Exception)
# сюда не сходят исключения типа GeneratorExit, KeyboardInterrupt, SystemExit
else:
# этот блок выполняется, если нет исключений
# если в этом блоке сделать return, он не будет вызван, пока не выполнился блок finally
finally:
# этот блок выполняется всегда, даже если нет исключений else будет проигнорирован
# если в этом блоке сделать return, то return в блоке

Подробнее о работе с исключениями в Python можно ознакомиться в официальной документации.

Исключения (exceptions) — ещё один тип данных в python. Исключения необходимы для того, чтобы сообщать программисту об ошибках.

Самый простейший пример исключения — деление на ноль:

>>> 100 / 0
Traceback (most recent call last):
  File "", line 1, in
    100 / 0
ZeroDivisionError: division by zero

Разберём это сообщение подробнее: интерпретатор нам сообщает о том, что он поймал исключение и напечатал информацию (Traceback (most recent call last)).

Далее имя файла (File «»). Имя пустое, потому что мы находимся в интерактивном режиме, строка в файле (line 1);

Выражение, в котором произошла ошибка (100 / 0).

Название исключения (ZeroDivisionError) и краткое описание исключения (division by zero).

Разумеется, возможны и другие исключения:

>>> 2 + '1'
Traceback (most recent call last):
  File "", line 1, in
    2 + '1'
TypeError: unsupported operand type(s) for +: 'int' and 'str'

>>> int('qwerty')
Traceback (most recent call last):
  File "", line 1, in
    int('qwerty')
ValueError: invalid literal for int() with base 10: 'qwerty'

В этих двух примерах генерируются исключения TypeError и ValueError соответственно. Подсказки дают нам полную информацию о том, где порождено исключение, и с чем оно связано.

Рассмотрим иерархию встроенных в python исключений, хотя иногда вам могут встретиться и другие, так как программисты могут создавать собственные исключения. Данный список актуален для python 3.3, в более ранних версиях есть незначительные изменения.

  • BaseException — базовое исключение, от которого берут начало все остальные.
    • SystemExit — исключение, порождаемое функцией sys.exit при выходе из программы.
    • KeyboardInterrupt — порождается при прерывании программы пользователем (обычно сочетанием клавиш Ctrl+C).
    • GeneratorExit — порождается при вызове метода close объекта generator.
    • Exception — а вот тут уже заканчиваются полностью системные исключения (которые лучше не трогать) и начинаются обыкновенные, с которыми можно работать.
      • StopIteration — порождается встроенной функцией next, если в итераторе больше нет элементов.
      • ArithmeticError — арифметическая ошибка.
        • FloatingPointError — порождается при неудачном выполнении операции с плавающей запятой. На практике встречается нечасто.
        • OverflowError — возникает, когда результат арифметической операции слишком велик для представления. Не появляется при обычной работе с целыми числами (так как python поддерживает длинные числа), но может возникать в некоторых других случаях.
        • ZeroDivisionError — деление на ноль.
      • AssertionError — выражение в функции assert ложно.
      • AttributeError — объект не имеет данного атрибута (значения или метода).
      • BufferError — операция, связанная с буфером, не может быть выполнена.
      • EOFError — функция наткнулась на конец файла и не смогла прочитать то, что хотела.
      • ImportError — не удалось импортирование модуля или его атрибута.
      • LookupError — некорректный индекс или ключ.
        • IndexError — индекс не входит в диапазон элементов.
        • KeyError — несуществующий ключ (в словаре, множестве или другом объекте).
      • MemoryError — недостаточно памяти.
      • NameError — не найдено переменной с таким именем.
        • UnboundLocalError — сделана ссылка на локальную переменную в функции, но переменная не определена ранее.
      • OSError — ошибка, связанная с системой.
        • BlockingIOError
        • ChildProcessError — неудача при операции с дочерним процессом.
        • ConnectionError — базовый класс для исключений, связанных с подключениями.
          • BrokenPipeError
          • ConnectionAbortedError
          • ConnectionRefusedError
          • ConnectionResetError
        • FileExistsError — попытка создания файла или директории, которая уже существует.
        • FileNotFoundError — файл или директория не существует.
        • InterruptedError — системный вызов прерван входящим сигналом.
        • IsADirectoryError — ожидался файл, но это директория.
        • NotADirectoryError — ожидалась директория, но это файл.
        • PermissionError — не хватает прав доступа.
        • ProcessLookupError — указанного процесса не существует.
        • TimeoutError — закончилось время ожидания.
      • ReferenceError — попытка доступа к атрибуту со слабой ссылкой.
      • RuntimeError — возникает, когда исключение не попадает ни под одну из других категорий.
      • NotImplementedError — возникает, когда абстрактные методы класса требуют переопределения в дочерних классах.
      • SyntaxError — синтаксическая ошибка.
        • IndentationError — неправильные отступы.
          • TabError — смешивание в отступах табуляции и пробелов.
      • SystemError — внутренняя ошибка.
      • TypeError — операция применена к объекту несоответствующего типа.
      • ValueError — функция получает аргумент правильного типа, но некорректного значения.
      • UnicodeError — ошибка, связанная с кодированием / раскодированием unicode в строках.
        • UnicodeEncodeError — исключение, связанное с кодированием unicode.
        • UnicodeDecodeError — исключение, связанное с декодированием unicode.
        • UnicodeTranslateError — исключение, связанное с переводом unicode.
      • Warning — предупреждение.

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

Первый пример применения этой конструкции:

>>> try:
...     k = 1 / 0
... except ZeroDivisionError:
...     k = 0
...
>>> print(k)
0

В блоке try мы выполняем инструкцию, которая может породить исключение, а в блоке except мы перехватываем их. При этом перехватываются как само исключение, так и его потомки. Например, перехватывая ArithmeticError, мы также перехватываем FloatingPointError, OverflowError и ZeroDivisionError.

>>> try:
...     k = 1 / 0
... except ArithmeticError:
...     k = 0
...
>>> print(k)
0

Также возможна инструкция except без аргументов, которая перехватывает вообще всё (и прерывание с клавиатуры, и системный выход и т. д.). Поэтому в такой форме инструкция except практически не используется, а используется except Exception. Однако чаще всего перехватывают исключения по одному, для упрощения отладки (вдруг вы ещё другую ошибку сделаете, а except её перехватит).

Ещё две инструкции, относящиеся к нашей проблеме, это finally и else. Finally выполняет блок инструкций в любом случае, было ли исключение, или нет (применима, когда нужно непременно что-то сделать, к примеру, закрыть файл). Инструкция else выполняется в том случае, если исключения не было.

>>> f = open('1.txt')
>>> ints = []
>>> try:
...     for line in f:
...         ints.append(int(line))
... except ValueError:
...     print('Это не число. Выходим.')
... except Exception:
...     print('Это что ещё такое?')
... else:
...     print('Всё хорошо.')
... finally:
...     f.close()
...     print('Я закрыл файл.')
...     # Именно в таком порядке: try, группа except, затем else, и только потом finally.
...
Это не число. Выходим.
Я закрыл файл.

Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Raising and Handling Python Exceptions

A Python program terminates as soon as it encounters an error. In Python, an error can be a syntax error or an exception. In this article, you will see what an exception is and how it differs from a syntax error. After that, you will learn about raising exceptions and making assertions. Then, you’ll finish with a demonstration of the try and except block.

An introduction to exceptions in Python

Exceptions versus Syntax Errors

Syntax errors occur when the parser detects an incorrect statement. Observe the following example:

>>> print( 0 / 0 ))
  File "<stdin>", line 1
    print( 0 / 0 ))
                  ^
SyntaxError: invalid syntax

The arrow indicates where the parser ran into the syntax error. In this example, there was one bracket too many. Remove it and run your code again:

>>> print( 0 / 0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

This time, you ran into an exception error. This type of error occurs whenever syntactically correct Python code results in an error. The last line of the message indicated what type of exception error you ran into.

Instead of showing the message exception error, Python details what type of exception error was encountered. In this case, it was a ZeroDivisionError. Python comes with various built-in exceptions as well as the possibility to create self-defined exceptions.

Raising an Exception

We can use raise to throw an exception if a condition occurs. The statement can be complemented with a custom exception.

Illustration of  raise statement usage

If you want to throw an error when a certain condition occurs using raise, you could go about it like this:

x = 10
if x > 5:
    raise Exception('x should not exceed 5. The value of x was: {}'.format(x))

When you run this code, the output will be the following:

Traceback (most recent call last):
  File "<input>", line 4, in <module>
Exception: x should not exceed 5. The value of x was: 10

The program comes to a halt and displays our exception to screen, offering clues about what went wrong.

The AssertionError Exception

Instead of waiting for a program to crash midway, you can also start by making an assertion in Python. We assert that a certain condition is met. If this condition turns out to be True, then that is excellent! The program can continue. If the condition turns out to be False, you can have the program throw an AssertionError exception.

Python assert statement

Have a look at the following example, where it is asserted that the code will be executed on a Linux system:

import sys
assert ('linux' in sys.platform), "This code runs on Linux only."

If you run this code on a Linux machine, the assertion passes. If you were to run this code on a Windows machine, the outcome of the assertion would be False and the result would be the following:

Traceback (most recent call last):
  File "<input>", line 2, in <module>
AssertionError: This code runs on Linux only.

In this example, throwing an AssertionError exception is the last thing that the program will do. The program will come to halt and will not continue. What if that is not what you want?

The try and except Block: Handling Exceptions

The try and except block in Python is used to catch and handle exceptions. Python executes code following the try statement as a “normal” part of the program. The code that follows the except statement is the program’s response to any exceptions in the preceding try clause.

Diagram showing try and except statements

As you saw earlier, when syntactically correct code runs into an error, Python will throw an exception error. This exception error will crash the program if it is unhandled. The except clause determines how your program responds to exceptions.

The following function can help you understand the try and except block:

def linux_interaction():
    assert ('linux' in sys.platform), "Function can only run on Linux systems."
    print('Doing something.')

The linux_interaction() can only run on a Linux system. The assert in this function will throw an AssertionError exception if you call it on an operating system other then Linux.

You can give the function a try using the following code:

try:
    linux_interaction()
except:
    pass

The way you handled the error here is by handing out a pass. If you were to run this code on a Windows machine, you would get the following output:

You got nothing. The good thing here is that the program did not crash. But it would be nice to see if some type of exception occurred whenever you ran your code. To this end, you can change the pass into something that would generate an informative message, like so:

try:
    linux_interaction()
except:
    print('Linux function was not executed')

Execute this code on a Windows machine:

Linux function was not executed

When an exception occurs in a program running this function, the program will continue as well as inform you about the fact that the function call was not successful.

What you did not get to see was the type of error that was thrown as a result of the function call. In order to see exactly what went wrong, you would need to catch the error that the function threw.

The following code is an example where you capture the AssertionError and output that message to screen:

try:
    linux_interaction()
except AssertionError as error:
    print(error)
    print('The linux_interaction() function was not executed')

Running this function on a Windows machine outputs the following:

Function can only run on Linux systems.
The linux_interaction() function was not executed

The first message is the AssertionError, informing you that the function can only be executed on a Linux machine. The second message tells you which function was not executed.

In the previous example, you called a function that you wrote yourself. When you executed the function, you caught the AssertionError exception and printed it to screen.

Here’s another example where you open a file and use a built-in exception:

try:
    with open('file.log') as file:
        read_data = file.read()
except:
    print('Could not open file.log')

If file.log does not exist, this block of code will output the following:

This is an informative message, and our program will still continue to run. In the Python docs, you can see that there are a lot of built-in exceptions that you can use here. One exception described on that page is the following:

Exception FileNotFoundError

Raised when a file or directory is requested but doesn’t exist. Corresponds to errno ENOENT.

To catch this type of exception and print it to screen, you could use the following code:

try:
    with open('file.log') as file:
        read_data = file.read()
except FileNotFoundError as fnf_error:
    print(fnf_error)

In this case, if file.log does not exist, the output will be the following:

[Errno 2] No such file or directory: 'file.log'

You can have more than one function call in your try clause and anticipate catching various exceptions. A thing to note here is that the code in the try clause will stop as soon as an exception is encountered.

Look at the following code. Here, you first call the linux_interaction() function and then try to open a file:

try:
    linux_interaction()
    with open('file.log') as file:
        read_data = file.read()
except FileNotFoundError as fnf_error:
    print(fnf_error)
except AssertionError as error:
    print(error)
    print('Linux linux_interaction() function was not executed')

If the file does not exist, running this code on a Windows machine will output the following:

Function can only run on Linux systems.
Linux linux_interaction() function was not executed

Inside the try clause, you ran into an exception immediately and did not get to the part where you attempt to open file.log. Now look at what happens when you run the code on a Linux machine:

[Errno 2] No such file or directory: 'file.log'

Here are the key takeaways:

  • A try clause is executed up until the point where the first exception is encountered.
  • Inside the except clause, or the exception handler, you determine how the program responds to the exception.
  • You can anticipate multiple exceptions and differentiate how the program should respond to them.
  • Avoid using bare except clauses.

The else Clause

In Python, using the else statement, you can instruct a program to execute a certain block of code only in the absence of exceptions.

Diagram of try, except, and else statements in Python

Look at the following example:

try:
    linux_interaction()
except AssertionError as error:
    print(error)
else:
    print('Executing the else clause.')

If you were to run this code on a Linux system, the output would be the following:

Doing something.
Executing the else clause.

Because the program did not run into any exceptions, the else clause was executed.

You can also try to run code inside the else clause and catch possible exceptions there as well:

try:
    linux_interaction()
except AssertionError as error:
    print(error)
else:
    try:
        with open('file.log') as file:
            read_data = file.read()
    except FileNotFoundError as fnf_error:
        print(fnf_error)

If you were to execute this code on a Linux machine, you would get the following result:

Doing something.
[Errno 2] No such file or directory: 'file.log'

From the output, you can see that the linux_interaction() function ran. Because no exceptions were encountered, an attempt to open file.log was made. That file did not exist, and instead of opening the file, you caught the FileNotFoundError exception.

Cleaning Up After Using finally

Imagine that you always had to implement some sort of action to clean up after executing your code. Python enables you to do so using the finally clause.

Diagram explaining try except else finally statements

Have a look at the following example:

try:
    linux_interaction()
except AssertionError as error:
    print(error)
else:
    try:
        with open('file.log') as file:
            read_data = file.read()
    except FileNotFoundError as fnf_error:
        print(fnf_error)
finally:
    print('Cleaning up, irrespective of any exceptions.')

In the previous code, everything in the finally clause will be executed. It does not matter if you encounter an exception somewhere in the try or else clauses. Running the previous code on a Windows machine would output the following:

Function can only run on Linux systems.
Cleaning up, irrespective of any exceptions.

Summing Up

After seeing the difference between syntax errors and exceptions, you learned about various ways to raise, catch, and handle exceptions in Python. In this article, you saw the following options:

  • raise allows you to throw an exception at any time.
  • assert enables you to verify if a certain condition is met and throw an exception if it isn’t.
  • In the try clause, all statements are executed until an exception is encountered.
  • except is used to catch and handle the exception(s) that are encountered in the try clause.
  • else lets you code sections that should run only when no exceptions are encountered in the try clause.
  • finally enables you to execute sections of code that should always run, with or without any previously encountered exceptions.

Hopefully, this article helped you understand the basic tools that Python has to offer when dealing with exceptions.

Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Raising and Handling Python Exceptions

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

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

Определение

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

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

Сегодня в Питоне выделяют такие типы ошибок:

  • синтаксические – причиной их возникновения становятся погрешности синтаксиса в коде;
  • логические – возникают из-за логических неточностей алгоритмов;
  • исключения – вызываются за счет некорректных действий юзеров или операционной системы.

Каждый вариант предусматривает собственные особенности и нюансы. Далее они будут рассмотрены более детально. Это поможет грамотно реагировать на баги и использовать имеющиеся знания для предельной результативности обработки исключений в Python.

Синтаксические

Возникают из-за того, что разработчик не соблюдал общепринятые нормы языка. Пример – пропустил круглую скобку в нужном месте или неграмотно прописал функции в блоке кодификации. Подобные погрешности хорошо отлавливаются посредством компилятора. Он сообщит программеру информацию об ошибке.

Вот пример, который покажет, что произойдет, если пропустить двойные кавычки при выводе строковых литералов:

Print (“Hello World!)

Программа при компиляции укажет следующий текст:

File “main.py”, line 1
Print (“Hello World!)
^
SyntaxError: EOL while scanning string literal

Логические

Это – наиболее сложный вариант для обнаружения. Связано это с тем, что отлов подобных неполадок не осуществляется при компиляции. Аналогичным образом дела обстоят и с готовым приложением.

Логические ошибки – это следствие недостатков в логике задействованных алгоритмов. Из-за этого программер не может достичь желаемого итога.

Print (avg(10, 20))

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

Исключения

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

Исключения обычно видно во время обработки кодификации. Ниже — пример, который указывает на невозможность проведения операции «деление на ноль»:

Print (10/0)

Если попытаться вывести на экран результат, утилита вызовет следующий код?

Результат выполнения программы – это исключительная ситуация. Следствием станет аварийное завершение работы и вывод ошибки на дисплей. Тут отобразится файл, а также номер строчки кода, где обнаружено исключение ZeroDivisionError. Ниже – это краткое описание бага (Division by zero).

Перехват

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

О перехвате необходимо запомнить следующее:

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

Вот пример попытки запуска утилиты, которая открывает текстовый документ:

Print (“Program started”)
Print (“Opening file…”)
F = open (“data.txt”)
Print (“Program finished”)

После того, как приложение будет обработано, на экране появится такая надпись:

Здесь происходит следующее:

  1. Print написано грамотно.
  2. Утилита не будет выполняться, так как файл с указанным именем не найден на жестком диске устройства.
  3. Программное обеспечение сгенерировало исключение FileNotFoundError. Оно указывает на неполадки при вводе или выводе.
  4. Последняя строчка кода – это сообщение о завершении утилиты. Она не отображается. Это свидетельствует о том, что далеко не все операции, предусмотренные ПО, будут выполнены из-за обнаруженного исключения.

Теперь можно рассмотреть блок try except. Он позволит обработать исключительную ситуацию без завершения уже запущенного приложения со стороны пользователя:

Print (“Program started”)
Try:
Print (“Opening file…”)
F = open (“data.txt”)
Except:
Print (“File not found”)
Print (“Program finished”)

Здесь:

  1. Блок try имеет опасную кодификацию, которая способна прервать функционирование утилиты.
  2. Except отвечает за инструкцию, которую необходимо обработать при баге.
  3. Программа вследствие обработки кодификации не завершается – об этом свидетельствует последний вывод функции print.

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

Несколько except

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

Вот – наглядный пример exceptions.

Вложенные и else

Блоки типа try except могут выступать в виде вложенных, чтобы обеспечивать гибкое управление исключениями в Python.

Для соответствующей кодификации требуется запомнить следующее:

  1. Она отвечает за демонстрацию попытки открытия текстового файла с записью в него определенной строки.
  2. Каждая цель задействует собственный блок try.
  3. Тут применяется конструкция с else, которая выполняется в случае, если в утилите нет корректировок.
  4. В приведенном примере else будет работать, если операция write будет успешно выполнена.
  5. По умолчанию документ открывается на чтение в текстовом режиме. При его открытии необходимо задействовать работу «w». Он отвечает за запись.

Если файла не было ранее на жестком диске, он будет создан в виде нового. В противном случае – производится перезапись. Все манипуляции выводятся на дисплей посредством print.

Finally

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

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

Чтобы упростить процесс коддинга, была разработана конструкция with/as. Она автоматизирует некоторые методы. Пример – закрытие файла заданного объекта. Результат все равно выводится через print.

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

Управление

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

Исключения пользователя

Обычно они в нужных ситуациях вызываются самостоятельно. Чтобы применить оные, необходимо задействовать ключевое слово raise. Далее указывается создание нового объекта типа Exception. Его удастся позже обработать через обычные конструкции try-except:

print("Program started")
try:
    raise Exception("User Exception!")
except Exception as e:
    print(str(e))
print("Program finished")

Program started
User Exception!
Program finished

При создании собственного исключения нужно запомнить следующее:

  1. Для этого требуется создать новый класс, который будет наследником базового типа Exception.
  2. Соответствующий прием позволит осуществлять запуск особых видов исключений в ситуациях, когда юзер ведет себя не по заданному алгоритму.
  3. В конструкторе Exception прописывается текст исключения.
  4. После его обработки и схватывания удается извлечь оный через str.

Вот пример с исключением типа raises NegativeAge. Отвечает за невозможность указания отрицательного возраста юзера:

Записывание в лог

В процессе изучения исключений, print, а также raise, recent call last, most recent call и иных особенностей Питона, может потребоваться запись в лог. Она помогает выводить спецсообщения, которые никак не отражаются на работоспособности утилиты.

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

Здесь:

  1. Клиенту доступны разные виды сообщений, которые реализуются не через print. Задействованы они обычно при разработке, лишний текст в консоли не отображается.
  2. Logging позволяет осуществлять запись в лог.
  3. Уровень INFO – это указатель на то, что сообщения уровней ниже (debug) не отображаются в логе.

Итоговый код:

В конце добавляется строка сообщения о типе одного сработавшего исключения в Python.

Иерархия

Исключения обладают строгой иерархией, которую нужно знать как print, in module, traceback и иные особенности языка. Вершина – это BaseException, который включает в себя все существующие типы исключений:

  • SystemExit – выход через sys.exit;
  • KeyboardInterrupt – прерывание пользователем;
  • GeneratorExit – отображается при вызове методы close для generator;
  • Exception – связь обычных несистемных исключений.

Здесь – таблица с исключениями несистемного плана в классе Exception.

Понравилась статья? Поделить с друзьями:
  • Обработка ошибок вба
  • Обработка ошибок python requests
  • Обработка ошибок laravel
  • Обработка ошибок delphi
  • Обработка ошибки отсутствия страницы