Обработка исключений в python

Traceback

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

Рассмотрим такой пример:

Traceback (most recent call last):
  File "/home/username/Develop/test/app.py", line 862, in _handle
    return route.call(**args)
  File "/home/username/Develop/test/app.py", line 1729, in wrapper
    rv = callback(*a, **ka)
  File "/home/username/Develop/test/__init__.py", line 76, in wrapper
    body = callback(*args, **kwargs)
  File "/home/username/Develop/test/my_app.py", line 16, in index
    raise Exception('test exception')

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

Рассмотрим какие еще встречаются комментарии к исключениям:

2 + '1'

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

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

int('qwerty')

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

Приведение строчки к целому числу приводит к исключению ValueError.

В трэйсе этих двух примеров можно прочесть, что в таком-то файле на такой-то строчке есть ошибки.

На этом список встроенных исключений не заканчивается, в следующем разделе рассмотрены основные исключения и причины их возникновения.

«Голое» исключение

Есть еще один способ поймать ошибку:

Python

try:
1 / 0
except:
print(«You cannot divide by zero!»)

1
2
3
4

try

1

except

print(«You cannot divide by zero!»)

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

Python

my_dict = {«a»:1, «b»:2, «c»:3}

try:
value = my_dict
except KeyError:
print(«That key does not exist!»)

1
2
3
4
5
6

my_dict={«a»1,»b»2,»c»3}

try

value=my_dict»d»

exceptKeyError

print(«That key does not exist!»)

Python

my_list =

try:
my_list
except IndexError:
print(«That index is not in the list!»)

1
2
3
4
5
6

my_list=1,2,3,4,5

try

my_list6

exceptIndexError

print(«That index is not in the list!»)

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

Помните, что списки в Пайтоне начинаются с нуля, так что когда вы говорите 6, вы запрашиваете 7. В любом случае, в нашем списке только пять объектов, по этой причине возникает IndexError, которую мы выявили. Вы также можете выявить несколько ошибок за раз при помощи одного оператора. Для этого существует несколько различных способов. Давайте посмотрим:

Python

my_dict = {«a»:1, «b»:2, «c»:3}

try:
value = my_dict
except IndexError:
print(«This index does not exist!»)
except KeyError:
print(«This key is not in the dictionary!»)
except:
print(«Some other error occurred!»)

1
2
3
4
5
6
7
8
9
10

my_dict={«a»1,»b»2,»c»3}

try

value=my_dict»d»

exceptIndexError

print(«This index does not exist!»)

exceptKeyError

print(«This key is not in the dictionary!»)

except

print(«Some other error occurred!»)

Это самый стандартный способ выявить несколько исключений. Сначала мы попробовали открыть доступ к несуществующему ключу, которого нет в нашем словаре. При помощи try/except мы проверили код на наличие ошибки KeyError, которая находится во втором операторе except

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

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

Python

try:
value = my_dict
except (IndexError, KeyError):
print(«An IndexError or KeyError occurred!»)

1
2
3
4

try

value=my_dict»d»

except(IndexError,KeyError)

print(«An IndexError or KeyError occurred!»)

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

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

В зависимости от сложности данной ошибки, вам может понадобиться выйти из программы. Иногда вам может понадобиться выполнить очистку, перед выходом из программы. Например, если вы открыли соединение с базой данных, вам нужно будет закрыть его, перед выходом из программы, или вы можете закончить с открытым соединением. Другой пример – закрытие дескриптора файла, к которому вы обращаетесь. Теперь нам нужно научиться убирать за собой. Это очень просто, если использовать оператор finally.

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

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

Начнем с обработки.

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

Давайте рассмотрим случай с делением на 0.

a = 100
b = 
c = a  b

Данный код приведет к исключению ZeroDivisionError. Чтобы этого не случилось, воспользуемся конструкцией try..except, например так:

try
    a = 100
    b = 
    c = a  b
except ZeroDivisionError as e
    print(e)

Если исполнить этот код, то на консоль будет выведена строка «integer division or modulo by zero». Казалось бы, что толком ничего это нам не дало, ошибка все также есть. Однако в блок except можно поместить обработку.

Например, мы условились, что значение переменной c в случае ошибки деления равно -1. Тогда модифицируем код:

try
    a = 100
    b = 
    c = a  b
except ZeroDivisionError as e
    c = -1

Перед тем как идти дальше, рассмотрим еще одну возможность.

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

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

try
    filepath = 'test_file.txt'
    with open(filepath, 'r') as fio
        result = fio.readlines()
    if not result
        raise Exception("File is empty")

except IOError as e
    result = []
except Exception as e
    result = []
    print(e)

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

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

try
    your_code
except (IOError, Exception) as e
    print(e)

Вызов исключений

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

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

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

Чтобы бросить исключение необходимо воспользоваться raise

Пример:

raise IOError("текст исключения")

где IOError это класс исключения.

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

try
    your_code
except Exception as e
    raise

Собственные исключения

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

В минимальном исполнении необходимо наследоваться от какого-нибудь класса в иерархии исключений. Например так:

class MyException(Exception):
    pass

Тогда можно бросить свое исключение:

raise MyException(Exception)

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

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

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

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

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

Base classes¶

The following exceptions are used mostly as base classes for other exceptions.

exception

The base class for all built-in exceptions. It is not meant to be directly
inherited by user-defined classes (for that, use ). If
is called on an instance of this class, the representation of
the argument(s) to the instance are returned, or the empty string when
there were no arguments.

The tuple of arguments given to the exception constructor. Some built-in
exceptions (like ) expect a certain number of arguments and
assign a special meaning to the elements of this tuple, while others are
usually called only with a single string giving an error message.

(tb)

This method sets tb as the new traceback for the exception and returns
the exception object. It is usually used in exception handling code like
this:

try
    ...
except SomeException
    tb = sys.exc_info()[2
    raise OtherException(...).with_traceback(tb)
exception

All built-in, non-system-exiting exceptions are derived from this class. All
user-defined exceptions should also be derived from this class.

exception

The base class for those built-in exceptions that are raised for various
arithmetic errors: , ,
.

exception

Raised when a related operation cannot be
performed.

Создание настраиваемого класса исключения

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

class EmployeeModuleError(Exception):
    """Base Exception Class for our Employee module"""
    pass


class EmployeeNotFoundError(EmployeeModuleError):
    """Error raised when employee is not found in the database"""

    def __init__(self, emp_id, msg):
        self.employee_id = emp_id
        self.error_message = msg


class EmployeeUpdateError(EmployeeModuleError):
    """Error raised when employee update fails"""

    def __init__(self, emp_id, sql_error_code, sql_error_msg):
        self.employee_id = emp_id
        self.error_message = sql_error_msg
        self.error_code = sql_error_code

По соглашению об именах к имени класса исключения добавляется суффикс «Ошибка».

Counter

Модуль collections также предоставляет нам небольшой аккуратный инструмент, который поддерживает быстрый и удобный в пользовании калькулятор. Этот инструмент называется Counter. Вы можете запустить его против большинства итерируемых. Давайте попробуем взглянуть на него в строке.

Python

from collections import Counter

a = Counter(‘superfluous’)

# Counter({‘u’: 3, ‘s’: 2, ‘e’: 1, ‘l’: 1, ‘f’: 1, ‘o’: 1, ‘r’: 1, ‘p’: 1})
print(a)

counter = Counter(‘superfluous’)
print(counter) # 3

1
2
3
4
5
6
7
8
9

fromcollectionsimportCounter

a=Counter(‘superfluous’)

 
# Counter({‘u’: 3, ‘s’: 2, ‘e’: 1, ‘l’: 1, ‘f’: 1, ‘o’: 1, ‘r’: 1, ‘p’: 1})

print(a)

counter=Counter(‘superfluous’)

print(counter’u’)# 3

В данном примере мы импортировали Counter из модуля collections и передали его строке. Это возвращает нам объект Counter, который является наследуемым классом словаря Python. Когда мы запускаем эту же команду, но назначаем её счетчик переменной, чтобы доступ к словарю был несколько проще. В данном случае, мы видим, что буква “u” встречается три раза в нашем примере. Класс Counter предоставляет несколько методов, которые могут вас заинтересовать. Например, вы можете вызывать элементы, которые будут выполнять итерацию над элементами, расположенными в словаре, но в произвольном порядке. Вы можете подумать, что эта функция является своего рода скремблером, так как выдача в этом случае представлена как скремблированная версия строки.

Python

print(list(counter.elements()))
#

1
2

print(list(counter.elements()))

#

Еще один полезный метод это most_common. Вы можете спросить Counter о том, какие объекты являются наиболее распространенными, передав число, отображающее наиболее часто повторяющие объекты “n”:

Python

print(counter.most_common(2))
#

1
2

print(counter.most_common(2))

#

Здесь мы попросили наш Counter выяснить, какие два объекта являются наиболее повторяемыми. Как вы видите, мы получили список кортежей, в котором указано, что “u” встречается 3 раза, а “s” – два раза. Еще один метод, который я хотел бы рассмотреть, это метод subtract.

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

Python

from collections import Counter

counter_one = Counter(‘superfluous’)

# Counter({‘u’: 3, ‘s’: 2, ‘e’: 1, ‘l’: 1, ‘f’: 1, ‘o’: 1, ‘r’: 1, ‘p’: 1})
print(counter_one)

counter_two = Counter(‘super’)
counter_one.subtract(counter_two)

print(counter_one)
# Counter({‘u’: 2, ‘l’: 1, ‘f’: 1, ‘o’: 1, ‘s’: 1, ‘e’: 0, ‘r’: 0, ‘p’: 0})

1
2
3
4
5
6
7
8
9
10
11
12

fromcollectionsimportCounter

counter_one=Counter(‘superfluous’)

 
# Counter({‘u’: 3, ‘s’: 2, ‘e’: 1, ‘l’: 1, ‘f’: 1, ‘o’: 1, ‘r’: 1, ‘p’: 1})

print(counter_one)

counter_two=Counter(‘super’)

counter_one.subtract(counter_two)

print(counter_one)

# Counter({‘u’: 2, ‘l’: 1, ‘f’: 1, ‘o’: 1, ‘s’: 1, ‘e’: 0, ‘r’: 0, ‘p’: 0})

Здесь мы создали заново наш первый счетчик и вывели его, чтобы узнать, что же в нем находится. Далее мы создали второй объект Counter. Наконец, мы вычли второй счетчик из первого. Если вы внимательно рассмотрите выдачу в конце, вы увидите, что количество букв для пяти объектов было уменьшено на одну. Как я заметил в начале раздела, вы можете использовать Counter против любых итерируемых или сопоставлений, так что вам не нужно использовать только строки. Вы можете также передать его кортежам, словарям и спискам! Попробуйте на практике, чтобы увидеть, как он работает с разными типами данных:
Теперь мы готовы к тому, чтобы перейти к defaultdict!

The try-finally Clause

You can use a finally: block along with a try: block. The finally: block is a place to put any code that must execute, whether the try-block raised an exception or not. The syntax of the try-finally statement is this −

try:
   You do your operations here;
   ......................
   Due to any exception, this may be skipped.
finally:
   This would always be executed.
   ......................

Note − You can provide except clause(s), or a finally clause, but not both. You cannot use else clause as well along with a finally clause.

Example

#!/usr/bin/python3

try:
   fh = open("testfile", "w")
   fh.write("This is my test file for exception handling!!")
finally:
   print ("Error: can\'t find file or read data")
   fh.close()

If you do not have permission to open the file in writing mode, then this will produce the following result −

Error: can't find file or read data

Same example can be written more cleanly as follows −

#!/usr/bin/python3

try:
   fh = open("testfile", "w")
   try:
      fh.write("This is my test file for exception handling!!")
   finally:
      print ("Going to close the file")
      fh.close()
except IOError:
   print ("Error: can\'t find file or read data")

This produces the following result −

Going to close the file

When an exception is thrown in the try block, the execution immediately passes to the finally block. After all the statements in the finally block are executed, the exception is raised again and is handled in the except statements if present in the next higher layer of the try-except statement.

5.1. Base classes¶

The following exceptions are used mostly as base classes for other exceptions.

exception

The base class for all built-in exceptions. It is not meant to be directly
inherited by user-defined classes (for that, use ). If
is called on an instance of this class, the representation of
the argument(s) to the instance are returned, or the empty string when
there were no arguments.

The tuple of arguments given to the exception constructor. Some built-in
exceptions (like ) expect a certain number of arguments and
assign a special meaning to the elements of this tuple, while others are
usually called only with a single string giving an error message.

(tb)

This method sets tb as the new traceback for the exception and returns
the exception object. It is usually used in exception handling code like
this:

try
    ...
except SomeException
    tb = sys.exc_info()[2
    raise OtherException(...).with_traceback(tb)
exception

All built-in, non-system-exiting exceptions are derived from this class. All
user-defined exceptions should also be derived from this class.

exception

The base class for those built-in exceptions that are raised for various
arithmetic errors: , ,
.

exception

Raised when a related operation cannot be
performed.

Raising an Exceptions

You can raise exceptions in several ways by using the raise statement. The general syntax for the raise statement is as follows.

Syntax

raise ]]

Here, Exception is the type of exception (for example, NameError) and argument is a value for the exception argument. The argument is optional; if not supplied, the exception argument is None.

The final argument, traceback, is also optional (and rarely used in practice), and if present, is the traceback object used for the exception.

Example

An exception can be a string, a class or an object. Most of the exceptions that the Python core raises are classes, with an argument that is an instance of the class. Defining new exceptions is quite easy and can be done as follows −

def functionName( level ):
   if level < 1:
      raise "Invalid level!", level
      # The code below to this would not be executed
      # if we raise the exception

Note: In order to catch an exception, an «except» clause must refer to the same exception thrown either class object or simple string. For example, to capture above exception, we must write the except clause as follows −

try:
   Business Logic here...
except "Invalid level!":
   Exception handling here...
else:
   Rest of the code here...

Handling an exception

If you have some suspicious code that may raise an exception, you can defend your program by placing the suspicious code in a try: block. After the try: block, include an except: statement, followed by a block of code which handles the problem as elegantly as possible.

Syntax

Here is simple syntax of try….except…else blocks −

try:
   You do your operations here
   ......................
except ExceptionI:
   If there is ExceptionI, then execute this block.
except ExceptionII:
   If there is ExceptionII, then execute this block.
   ......................
else:
   If there is no exception then execute this block. 

Here are few important points about the above-mentioned syntax −

  • A single try statement can have multiple except statements. This is useful when the try block contains statements that may throw different types of exceptions.

  • You can also provide a generic except clause, which handles any exception.

  • After the except clause(s), you can include an else-clause. The code in the else-block executes if the code in the try: block does not raise an exception.

  • The else-block is a good place for code that does not need the try: block’s protection.

Example

This example opens a file, writes content in the, file and comes out gracefully because there is no problem at all −

#!/usr/bin/python3

try:
   fh = open("testfile", "w")
   fh.write("This is my test file for exception handling!!")
except IOError:
   print ("Error: can\'t find file or read data")
else:
   print ("Written content in the file successfully")
   fh.close()

This produces the following result −

Written content in the file successfully

Example

This example tries to open a file where you do not have the write permission, so it raises an exception −

#!/usr/bin/python3

try:
   fh = open("testfile", "r")
   fh.write("This is my test file for exception handling!!")
except IOError:
   print ("Error: can\'t find file or read data")
else:
   print ("Written content in the file successfully")

This produces the following result −

Error: can't find file or read data

8.1. Синтаксические ошибки

Синтаксические ошибки (Syntax Errors), также известны как ошибки грамматического разбора (parsing errors), являются, пожалуй, наиболее распространенным видом жалоб пока вы все еще изучаете Python:

Синтаксический анализатор (parser — парсер) повторяет ошибочную строку и отображает маленькую «стрелку», указывая тем самым, что в предшествующей строке была обнаружена ошибка. Ошибка вызвана (или хотя бы обнаружилась) объектом предшествующим стрелке: в примере, ошибка обнаружена в функции , так как перед ней отсутствует двоеточие (). Имя файла и номер строки выводятся, чтобы вы знали, где искать в случае, если ввод был получен из скрипта.

Warnings¶

The following exceptions are used as warning categories; see the
documentation for more details.

exception

Base class for warning categories.

exception

Base class for warnings generated by user code.

exception

Base class for warnings about deprecated features when those warnings are
intended for other Python developers.

exception

Base class for warnings about features which are obsolete and
expected to be deprecated in the future, but are not deprecated
at the moment.

This class is rarely used as emitting a warning about a possible
upcoming deprecation is unusual, and
is preferred for already active deprecations.

exception

Base class for warnings about dubious syntax.

exception

Base class for warnings about dubious runtime behavior.

exception

Base class for warnings about deprecated features when those warnings are
intended for end users of applications that are written in Python.

exception

Base class for warnings about probable mistakes in module imports.

exception

Base class for warnings related to Unicode.

exception

Base class for warnings related to and .

Catching Exceptions in Python

In Python, exceptions can be handled using a statement.

The critical operation which can raise an exception is placed inside the clause. The code that handles the exceptions is written in the clause.

We can thus choose what operations to perform once we have caught the exception. Here is a simple example.

Output

The entry is a
Oops! <class 'ValueError'> occurred.
Next entry.

The entry is 0
Oops! <class 'ZeroDivisionError'> occured.
Next entry.

The entry is 2
The reciprocal of 2 is 0.5

In this program, we loop through the values of the randomList list. As previously mentioned, the portion that can cause an exception is placed inside the block.

If no exception occurs, the block is skipped and normal flow continues(for last value). But if any exception occurs, it is caught by the block (first and second values).

Here, we print the name of the exception using the function inside module. We can see that causes and causes .

Since every exception in Python inherits from the base class, we can also perform the above task in the following way:

This program has the same output as the above program.

Добавить комментарий

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

Adblock
detector