# Исключения

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

Это касается и программ, содержащих недействительные команды. В этом случае 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).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://exceedteam.gitbook.io/python-and-back-end/python-language/isklyucheniya.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
