Все виды ошибок питон

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

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

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

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

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

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

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

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

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

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

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

a = 8
b = 10
c = a b
File "<ipython-input-8-3b3ffcedf995>", 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)

<ipython-input-3-c6e0f7eb0cde> in <module>
----> 1 recursion()


<ipython-input-2-5395140f7f05> in recursion()
      1 def recursion():
----> 2     return recursion()


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


<ipython-input-2-5395140f7f05> in recursion()
      1 def recursion():
----> 2     return recursion()


RecursionError: maximum recursion depth exceeded

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

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

Пример:

for i in range(10):
    print('Привет Мир!')
  File "<ipython-input-6-628f419d2da8>", line 2
    print('Привет Мир!')
        ^
IndentationError: expected an indented block

Исключения

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

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

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

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

TypeError                                 Traceback (most recent call last)

<ipython-input-7-86a706a0ffdf> in <module>
      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)

<ipython-input-43-e9e866a10e2a> in <module>
----> 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)

<ipython-input-6-9e567e3ae964> in <module>
----> 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)

<ipython-input-1-57792b6bc7e4> in <module>
     14
     15 SubClass().do_something()
---> 16 BaseClass().do_something()


<ipython-input-1-57792b6bc7e4> 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 их обрабатывает.

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

Обработка исключений 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 (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 можно ознакомиться в официальной документации.

Исключения#

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

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

---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_13252/2354412189.py in <module>
----> 1 1/0

ZeroDivisionError: division by zero

Как видимо, появилось сообщение об ошибке. Последняя строка этого сообщения, говорит, что конкретно произошло.

  • Исключения бывают разных типов и конкретный тип брошенного исключения печатается до двоеточия. В данном случае это ZeroDivisionError.

  • После типа ошибки выводится дополнительная информация, которая может зависеть от типа ошибки и причины её возникновения.

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

def f():
    1/0

def g():
    f()

g()
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_13252/4200539898.py in <module>
      5     f()
      6 
----> 7 g()

~\AppData\Local\Temp/ipykernel_13252/4200539898.py in g()
      3 
      4 def g():
----> 5     f()
      6 
      7 g()

~\AppData\Local\Temp/ipykernel_13252/4200539898.py in f()
      1 def f():
----> 2     1/0
      3 
      4 def g():
      5     f()

ZeroDivisionError: division by zero

В этот раз сообщение об ошибке гораздо длиннее, потому что ошибка возникла не на самом верхнем уровне, а на уровне функции.

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

В python есть приличное количество встроенных исключений, которые интерпретатор бросает при обнаружении ошибки во время исполнения. Например, если попытаться обратиться к необъявленной переменной (несуществующему имени), то возникнет ошибка NameError. Обращение к неправильному атрибуту вызовет ошибку AttributeError, обращение за пределы списка — IndexError, поиск в словаре по несуществующему ключу — KeyError и т.д.

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

В python всё является объектом, в том числе и исключения. При этом у каждого исключения есть свой тип, примеры которых уже обсуждалось выше: NameError, AttributeError, StopIteration и т.д. — это всё тип исключения. Обнаружение ошибки приводит к тому, что создаётся экземпляр исключения соответствующего типа и это исключение начинает распространяться. При этом все встроенные типы исключений находятся в иерархии, с которой можно ознакомиться по ссылке.

Так, на самом верху располагается базовый класс BaseException от которого наследуется все остальные типы исключений. На втором этаже располагаются исключения связанные с завершением исполнения программы (например, SystemExit возникает при вызове функции sys.exit(), KeyboardInterrupt возникает, если пользователь нажимает прерывающую комбинацию клавиш во время работы приложения), а так же класс Exception, который и является базовым классом для всех исключений не связанных с завершением программы.

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

Возбуждение исключений. raise#

Многие исключения бросаются интерпретатором python при обнаружении ошибки или в служебных целях. Для этого используется ключевое слово raise справа от которого указывается экземпляр исключения.

raise Exception("Моё первое исключение!")
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_13252/2359356075.py in <module>
----> 1 raise Exception("Моё первое исключение!")

Exception: Моё первое исключение!

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

def f():
    print("До исключения!")
    raise Exception("Исключение!")
    print("После исключения!")

f()
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_13252/2985915162.py in <module>
      4     print("После исключения!")
      5 
----> 6 f()

~\AppData\Local\Temp/ipykernel_13252/2985915162.py in f()
      1 def f():
      2     print("До исключения!")
----> 3     raise Exception("Исключение!")
      4     print("После исключения!")
      5 

Exception: Исключение!

Видим, что инструкция print("До исключения!") исполнилась, а инструкция print("После исключения") — нет, т.к. сразу после возбуждения исключения нормальный поток исполнения программы прекратился и исключение начало распространяться. Исключение распространяется наверх по стеку вызовов, пока не встретится блок, обрабатывающий это исключение. Если такой блок так и не встречается, то выводится сообщение об ошибке и программа падает (или может продолжить ожидание инструкций от пользователя в интерактивном режиме, что происходит, например, в jupyter ноутбуках).

Обработка исключений.#

Обработка ошибок в python осуществляется с помощью инструкции try, которая может встречаться в блоках двух видов:

  • try/except;

  • try/finally;

Блок try/except#

С помощью блока try/except можно обрабатывать конкретные типы исключений. В самой простой своей форме он выглядит примерно следующим образом.

try:
    инструкции
except ТипИсключения:
    инструкции

После ключевого слова try помещаются инструкции, исполнение которых может привести к возникновению исключения. Инструкции в блоке except выполняются только, если а) вовремя выполнения инструкций в блоке try возникла ошибка и б) её тип совпал с указанным типом.

В качестве примера опять поделим на ноль, но в этот раз поместим это деление в блок try/except и обработаем исключение типа ZeroDivisionError.

try:
    1/0
    print("После попытки деления.")
except ZeroDivisionError:
    print("Перехвачена ошибка деления на ноль!")
Перехвачена ошибка деления на ноль!

Видим, что

  1. сообщения об ошибке с типом ошибки и трассировочной информацией не появилось, т.к. ошибка была обработана;

  2. вместо этого выполнилась инструкция print в блоке except ZeroDivisionError;

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

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

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

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

try:
    try:
        1/0
    except NameError:
        print("Ошибка перехвачена внутренним блоком try/except.")
except ZeroDivisionError:
    print("Ошибка перехвачена внешним блоком try/except.")

print("Инструкция после обработки исключения.")
Ошибка перехвачена внешним блоком try/except.
Инструкция после обработки исключения.

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

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

try:
    try:
        1/0
    except ZeroDivisionError:
        print("Ошибка перехвачена внутренним блоком except.")
except ZeroDivisionError:
    print("Ошибка перехвачена внешним блоком except.")

print("Инструкция после обработки исключения.")
Ошибка перехвачена внутренним блоком except.
Инструкция после обработки исключения.

Здесь внутренний блок try/except обрабатывает правильный тип исключения. Возникшее исключение прекращает распространяться в этом внутреннем блоке try/except и выполняется инструкция print во внутреннем блоке except. Т.к. исполнение инструкций после внешнего ключевого слова try проходит без ошибок (ошибка возникает, но внешний блок её не замечает, т.к. она обрабатывается внутренним блоком try/except), то внешний блок except игнорируется.

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

try:
    1/0
except NameError:
    print("Перехвачена ошибка NameError")
except ZeroDivisionError:
    print("Перехвачена ошибка ZeroDivisionError")
Перехвачена ошибка ZeroDivisionError

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

Tip

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

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

try:
    1/0
except Exception:
    print("Перехвачено неожиданное исключение!")
except ZeroDivisionError:
    print("Перехвачено исключение ZeroDivisionError!")
Перехвачено неожиданное исключение!

Здесь в блоке try возникает исключение ZeroDivisionError и начинает распространяться. В блоке except Exception проверка на тип успешно проходит (иерархия между ними выглядит так: Exception \(\to\) ArithmeticError \(\to\) ZeroDivisionError), а значит вызывается инструкции в соответствующем блоке, а до блока except ZeroDivisionError дело так и не доходит: ошибка обработана ранее.

Правильнее в данном случае было бы поменять эти блоки местами.

try:
    1/0
except ZeroDivisionError:
    print("Перехвачено исключение ZeroDivisionError!")
except Exception:
    print("Перехвачено неожиданное исключение!")
Перехвачено исключение ZeroDivisionError!

Дополнительные возможности except#

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

except ТипИсключения as цель:
    операции над цель

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

try:
    raise Exception("Сообщение исключения!")
except Exception as e:
    print(e)

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

try:
    1/0
except (ZeroDivisionError, NameError):
    print("Возникло исключение ZeroDivisionError или NameError!")
Возникло исключение ZeroDivisionError или NameError!

Так же, в самом конце блока try/except можно добавить блок else, инструкции в котором исполняться, только если блок try завершится без ошибок. Чтобы продемонстрировать это, определим функцию divide, которая будет пытаться делить первый аргумент на второй и возвращать результат деления, если деление произошло успешно, и возвращать бесконечность, если второй параметр равен нулю.

def divide(a, b):
    try:
        result = a / b
    except ZeroDivisionError:
        print("Попытка деления на ноль.")
        return float("inf")
    else:
        print("Деление произошло успешно.")
        return result

Блок else в этой функции исполнится, только если в блоке try деление произойдет успешно, т.е. если не возникнет ошибки деления на ноль.

print(divide(42, 14))
print(divide(42, 0))
Деление произошло успешно.
3.0
Попытка деления на ноль.
inf

Блок try/finally#

Синтаксис блока try/except выглядит следующим образом.

try:
    инструкции
finally:
    инструкции

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

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

Например, раньше часто встречались конструкции следующего вида.

f = open(some_file, "w")
try:
    do_something_with_file(f)
finally:
    f.close()

Т.е. операции над открытым файлом производились в блоке try/finally, что гарантировало, что файл будет закрыт, даже если возникнет исключение. Сегодня гораздо удобнее использовать контекстные менеджеры with для таких целей.

Блок try/except/finally#

Если встречается блок обработки ошибки, в котором встречаются и except и finally, то его можно представить в виде вложения блока try/except внутрь блока try/finally. Иными словами, следующие две конструкции ведут себя одинаково.

try:
    инструкции
except SomeException:
    инструкции
else:
    инструкции
finally:
    инструкции
try:
    try:
        инструкции
    except SomeException:
        инструкции
    else:
        инструкции
finally:
    инструкции

Для демонстрации работы блока такого вида приведем пример из документации.

def divide(x, y):
    try:
        result = x / y
    except ZeroDivisionError:
        print("Попытка деления на ноль!")
    else:
        print(f"Результат деления {result}")
    finally:
        print("Выполнение блока 'finally'.")

Функция divide пытается поделить x на y.

Если деление происходит успешно (else), то выводится результат.

Результат деления 2.0
Выполнение блока 'finally'.

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

Попытка деления на ноль!
Выполнение блока 'finally'.

Если возникает ошибка другого рода, то её распространение продолжается.

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_13252/1759864827.py in <module>
----> 1 divide("2", "1")

~\AppData\Local\Temp/ipykernel_13252/3246861353.py in divide(x, y)
      1 def divide(x, y):
      2     try:
----> 3         result = x / y
      4     except ZeroDivisionError:
      5         print("division by zero!")

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

Блок finally выполняется в любом случае.

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

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

  1. Синтаксические.
  2. Логические (исключения).

Ошибки синтаксиса

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

Давайте посмотрим на один пример:

>>> if a < 3
  File "<interactive input>", line 1
    if a < 3
           ^
SyntaxError: invalid syntax

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

Мы можем заметить здесь, что в операторе if отсутствует двоеточие.

Логические ошибки (исключения)

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

Например, они возникают, когда мы пытаемся открыть файл (для чтения), который не существует (FileNotFoundError), пытаемся разделить число на ноль (ZeroDivisionError) или импортировать несуществующий модуль (ImportError).

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

Давайте посмотрим, как Python обрабатывает эти ошибки:

>>> 1 / 0
Traceback (most recent call last):
 File "<string>", line 301, in runcode
 File "<interactive input>", line 1, in <module>
ZeroDivisionError: division by zero

>>> open("imaginary.txt")
Traceback (most recent call last):
 File "<string>", line 301, in runcode
 File "<interactive input>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'imaginary.txt'

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

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

print(dir(locals()['__builtins__']))

locals() [‘__ builtins__’] вернет модуль встроенных исключений, функций и атрибутов. dir позволяет нам перечислять эти атрибуты в виде строк.

Ниже перечислены некоторые из распространенных встроенных исключений в программировании на Python, а также ошибки, вызывающие их:

Исключение Причина ошибки
AssertionError Возникает при сбое утверждения утверждения.
AttributeError Возникает при сбое присвоения атрибута или ссылки.
EOFError Возникает, когда функция input() достигает условия конца файла.
FloatingPointError Возникает при сбое операции с плавающей запятой.
ГенераторВыход Поднимается при вызове метода генератора close().
ImportError Возникает, когда импортированный модуль не найден.
IndexError Возникает, когда индекс последовательности выходит за пределы допустимого диапазона.
KeyError Возникает, когда ключ не найден в словаре.
KeyboardInterrupt Возникает, когда пользователь нажимает клавишу прерывания (Ctrl + C или Delete).
MemoryError Возникает, когда для операции не хватает памяти.
NameError Возникает, когда переменная не найдена в локальной или глобальной области.
NotImplementedError Поднят абстрактными методами.
OSError Возникает, когда работа системы вызывает ошибку, связанную с системой.
OverflowError Возникает, когда результат арифметической операции слишком велик для представления.
ReferenceError Возникает, когда слабый ссылочный прокси используется для доступа к референту со сборкой мусора.
Ошибка выполнения Возникает, когда ошибка не попадает ни в одну другую категорию.
StopIteration Вызывается функцией next(), чтобы указать, что итератор больше не возвращает элемент.
Синтаксическая ошибка Возникает парсером при обнаружении синтаксической ошибки.
IndentationError Возникает при неправильном отступе.
TabError Возникает, когда отступ состоит из несовместимых знаков табуляции и пробелов.
Системная ошибка Возникает, когда интерпретатор обнаруживает внутреннюю ошибку.
SystemExit Вызывается функцией sys.exit().
TypeError Возникает, когда функция или операция применяется к объекту неправильного типа.
UnboundLocalError Возникает, когда делается ссылка на локальную переменную в функции или методе, но с этой переменной не привязано никакого значения.
UnicodeError Возникает при возникновении ошибки кодирования или декодирования, связанной с Unicode.
UnicodeEncodeError Возникает, когда во время кодирования возникает ошибка, связанная с Unicode.
UnicodeDecodeError Возникает, когда во время декодирования возникает ошибка, связанная с Unicode.
UnicodeTranslateError Возникает, когда во время перевода возникает ошибка, связанная с Unicode.
ValueError Возникает, когда функция получает аргумент правильного типа, но неправильное значение.
ZeroDivisionError Возникает, когда второй операнд деления или операции по модулю равен нулю.

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

Встроенные и определяемые пользователем исключения в Python можно обрабатывать с помощью операторов try, except и finally.

47414cookie-checkОшибки и встроенные исключения в Python

Понравилась статья? Поделить с друзьями:
  • Всд ошибка число как исправить
  • Все кто совершают подвиги следуют зову сердца ошибка
  • Время истекло ошибка майнкрафт
  • Все возвращается на спирали своя лексическая ошибка
  • Все делают ошибки на английском