> For the complete documentation index, see [llms.txt](https://exceedteam.gitbook.io/python-and-back-end/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://exceedteam.gitbook.io/python-and-back-end/python-language/isklyucheniya.md).

# Исключения

Исключения возникают тогда, когда в программе возникает некоторая *исключительная* ситуация. Например, к чему приведёт попытка чтения несуществующего файла? Или если файл был случайно удалён, пока программа работала? Такие ситуации обрабатываются при помощи **исключений**.

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

### Ошибки

Рассмотрим простой вызов функции `print`. Что, если мы ошибочно напишем `print` как `Print`? Обратите внимание на заглавную букву. В этом случае Python *поднимает* синтаксическую ошибку.

```python
>>> Print('Привет, Мир!')
Traceback (most recent call last):
  File "<pyshell#0>", line 1, in <module>
    Print('Привет, Мир!')
NameError: name 'Print' is not defined
>>> print('Привет, Мир!')
Привет, Мир!
```

Обратите внимание, что была поднята ошибка `NameError`, а также указано место, где была обнаружена ошибка. Так в данном случае действует *обработчик ошибок*.

### Исключения

**Попытаемся** считать что-либо от пользователя. Нажмите `Сtrl-D` (или `Ctrl+Z` в Windows) и посмотрите, что произойдёт.

```python
>>> s = input('Введите что-нибудь --> ')
Введите что-нибудь -->
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    s = input('Введите что-нибудь --> ')
EOFError: EOF when reading a line
```

Python поднимает ошибку с именем `EOFError`, что означает, что он обнаружил символ *конца файла* (который вводится при помощи `Ctrl-D`) там, где не ожидал.

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

Обрабатывать исключения можно при помощи оператора `try..except`. При этом все обычные команды помещаются внутрь try-блока, а все обработчики исключений – в except-блок.

**Пример:** (сохраните как `try_except.py`)

```python
try:
    text = input('Введите что-нибудь --> ')
except EOFError:
    print('Ну зачем вы сделали мне EOF?')
except KeyboardInterrupt:
    print('Вы отменили операцию.')
else:
    print('Вы ввели {0}'.format(text))
```

**Вывод:**

```python
$ python3 try_except.py
Введите что-нибудь -->     # Нажмите ctrl-d
Ну зачем вы сделали мне EOF?

$ python3 try_except.py
Введите что-нибудь -->     # Нажмите ctrl-c
Вы отменили операцию.

$ python3 try_except.py
Введите что-нибудь --> без ошибок
Вы ввели без ошибок
```

**Как это работает:**

> Здесь мы поместили все команды, которые могут вызвать исключения/ошибки, внутрь блока `try`, а затем поместили обработчики соответствующих ошибок/исключений в блок `except`. Выражение `except` может обрабатывать как одиночную ошибку или исключение, так и список ошибок/исключений в скобках. Если не указано имя ошибки или исключения, обрабатываться будут *все* ошибки и исключения.

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

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

Можно также добавить пункт `else` к соответствующему блоку `try..except`. Этот пункт будет выполнен тогда, когда исключений не возникает.

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

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

Исключение можно *поднять* при помощи оператора `raise`, передав ему имя ошибки/исключения, а также объект исключения, который нужно *выбросить*.

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

**Пример:** (сохраните как `raising.py`)

```python
class ShortInputException(Exception):
    '''Пользовательский класс исключения.'''
    def __init__(self, length, atleast):
        Exception.__init__(self)
        self.length = length
        self.atleast = atleast

try:
    text = input('Введите что-нибудь --> ')
    if len(text) < 3:
        raise ShortInputException(len(text), 3)
    # Здесь может происходить обычная работа
except EOFError:
    print('Ну зачем вы сделали мне EOF?')
except ShortInputException as ex:
    print('ShortInputException: Длина введённой строки -- {0}; \
           ожидалось, как минимум, {1}'.format(ex.length, ex.atleast))
else:
    print('Не было исключений.')
```

**Вывод:**

```python
$ python3 raising.py
Введите что-нибудь --> а
ShortInputException: Длина введённой строки -- 1; ожидалось, как минимум, 3

$ python3 raising.py
Введите что-нибудь --> абв
Не было исключений.
```

**Как это работает:**

> Здесь мы создаём наш собственный тип исключения. Этот новый тип исключения называется `ShortInputException`. Он содержит два поля: `length`, хранящее длину введённого текста, и `atleast`, указывающее, какую минимальную длину текста ожидала программа.
>
> В пункте `except` мы указываем класс ошибки `ShortInputException`, который будет сохранён как переменная `ex`, содержащая соответствующий объект ошибки/исключения. Это аналогично параметрам и аргументам при вызове функции. Внутри этого пункта `except` мы используем поля `length` и `atleast` объекта исключения для вывода необходимых сообщений пользователю.

### Try .. Finally

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

Сохраните как `finally.py`:

```python
import time

try:
    f = open('poem.txt')
    while True: # наш обычный способ читать файлы
        line = f.readline()
        if len(line) == 0:
            break
        print(line, end='')
        time.sleep(2) # Пусть подождёт некоторое время
except KeyboardInterrupt:
    print('!! Вы отменили чтение файла.')
finally:
    f.close()
    print('(Очистка: Закрытие файла)')
```

**Вывод:**

```python
$ python3 finally.py
Программировать весело
Если работа скучна,
Чтобы придать ей весёлый тон -
!! Вы отменили чтение файла.
(Очистка: Закрытие файла)
```

**Как это работает:**

> Здесь мы производим обычные операции чтения из файла, но в данном случае добавляем двухсекундный сон после вывода каждой строки при помощи функции `time.sleep`, чтобы программа выполнялась медленно (ведь Python очень быстр от природы). Во время выполнения программы нажмите `ctrl-c`, чтобы прервать/отменить выполнение программы.
>
> Пронаблюдайте, как при этом выдаётся исключение `KeyboardInterrupt`, и программа выходит. Однако, прежде чем программа выйдет, выполняется пункт `finally`, и файловый объект будет всегда закрыт.

### Оператор with

Типичной схемой является запрос некоторого ресурса в блоке `try` с последующим освобождением этого ресурса в блоке `finally`. Для того, чтобы сделать это более “чисто”, существует оператор `with`:

Сохраните как `using_with.py`:

```python
with open("poem.txt") as f:
    for line in f:
        print(line, end='')
```

**Как это работает:**

> Вывод должен быть таким же, как и в предыдущем примере. Разница лишь в том, что здесь мы используем функцию `open` с оператором `with` – этим мы оставляем автоматическое закрытие файла под ответственность `with open`.
>
> За кулисами происходит следующее. Существует некий протокол, используемый оператором `with`. Он считывает объект, возвращаемый оператором `open`. Назовём его в данном случае “thefile”.
>
> Перед запуском блока кода, содержащегося в нём, оператор `with` *всегда* вызывает функцию `thefile.__enter__`, а также *всегда* вызывает `thefile.__exit__` после завершения выполнения этого блока кода.
>
> Так что код, который мы бы написали в блоке `finally`, будет автоматически обработан методом `__exit__`. Это избавляет нас от необходимости повторно в явном виде указывать операторы `try..finally`.
>
> Более обширное рассмотрение этой темы выходит за рамки настоящей книги, поэтому для более исчерпывающего объяснения см. [**PEP 343**](https://www.python.org/dev/peps/pep-0343).
