> **Источник:** https://python-all.ru/3.9/tutorial/errors.html
>
> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.

---

# 8. Ошибки и исключения

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

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

Синтаксические ошибки, также известные как ошибки разбора, – пожалуй, самый распространённый вид неполадок, с которыми вы сталкиваетесь, пока учите Python:

```python
>>> while True print('Hello world')
  File "<stdin>", line 1
    while True print('Hello world')
                   ^
SyntaxError: invalid syntax
```

Парсер повторяет проблемную строку и показывает маленькую «стрелку», указывающую на самую раннюю позицию в строке, где была обнаружена ошибка. Ошибка вызвана (или, по крайней мере, обнаружена) элементом *предшествующим* стрелке: в примере ошибка обнаружена у функции [`print()`](https://python-all.ru/3.9/library/functions.html#print), поскольку перед ней отсутствует двоеточие (`':'`). Имя файла и номер строки выводятся, чтобы было понятно, где искать, если входные данные поступили из сценария.

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

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

```python
>>> 10 * (1/0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str
```

Последняя строка сообщения об ошибке показывает, что произошло. Исключения бывают разных типов, и тип выводится как часть сообщения: в примере это [`ZeroDivisionError`](https://python-all.ru/3.9/library/exceptions.html#ZeroDivisionError), [`NameError`](https://python-all.ru/3.9/library/exceptions.html#NameError) и [`TypeError`](https://python-all.ru/3.9/library/exceptions.html#TypeError). Строка, выводимая как тип исключения, – это имя встроенного исключения, которое произошло. Это верно для всех встроенных исключений, но не обязательно для определяемых пользователем (хотя это полезное соглашение). Стандартные имена исключений – встроенные идентификаторы (не зарезервированные ключевые слова).

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

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

[Встроенные исключения](https://python-all.ru/3.9/library/exceptions.html#bltin-exceptions) содержит список встроенных исключений и их значение.

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

Можно писать программы, обрабатывающие выбранные исключения. В следующем примере показан запрос ввода у пользователя до тех пор, пока не будет введено целое число, но при этом пользователь может прервать программу (с помощью `Control-C` или того, что поддерживает операционная система); обратите внимание, что прерывание пользователем вызывает исключение [`KeyboardInterrupt`](https://python-all.ru/3.9/library/exceptions.html#KeyboardInterrupt).

```python
>>> while True:
...     try:
...         x = int(input("Please enter a number: "))
...         break
...     except ValueError:
...         print("Oops!  That was no valid number.  Try again...")
...
```

Инструкция [`try`](https://python-all.ru/3.9/reference/compound_stmts.html#try) работает следующим образом.

- Сначала выполняется *try-блок* (инструкции между ключевыми словами [`try`](https://python-all.ru/3.9/reference/compound_stmts.html#try) и [`except`](https://python-all.ru/3.9/reference/compound_stmts.html#except)).
- Если исключение не произошло, *except-блок* пропускается, и выполнение инструкции [`try`](https://python-all.ru/3.9/reference/compound_stmts.html#try) завершается.
- Если при выполнении блока try возникает исключение, оставшаяся часть блока пропускается. Затем, если его тип совпадает с исключением, указанным после [`except`](https://python-all.ru/3.9/reference/compound_stmts.html#except) ключевого слова, выполняется блок except, и затем выполнение продолжается после оператора [`try`](https://python-all.ru/3.9/reference/compound_stmts.html#try).
- Если возникает исключение, которое не соответствует исключению, указанному в блоке except, оно передается внешним операторам [`try`](https://python-all.ru/3.9/reference/compound_stmts.html#try); если обработчик не найден, это *необработанное исключение* и выполнение останавливается с сообщением, как показано выше.

Оператор [`try`](https://python-all.ru/3.9/reference/compound_stmts.html#try) может содержать более одного блока except для указания обработчиков разных исключений. Будет выполнен не более одного обработчика. Обработчики обрабатывают только исключения, возникшие в соответствующем блоке try, а не в других обработчиках того же оператора `try`. Блок except может указывать несколько исключений в виде кортежа в скобках, например:

```python
... except (RuntimeError, TypeError, NameError):
...     pass
```

Класс в блоке [`except`](https://python-all.ru/3.9/reference/compound_stmts.html#except) совместим с исключением, если это тот же класс или его базовый класс (но не наоборот – блок except, содержащий производный класс, несовместим с базовым классом). Например, следующий код выведет B, C, D в таком порядке:

```python
class B(Exception):
    pass

class C(B):
    pass

class D(C):
    pass

for cls in [B, C, D]:
    try:
        raise cls()
    except D:
        print("D")
    except C:
        print("C")
    except B:
        print("B")
```

Обратите внимание, что если бы блоки except были переставлены (с `except B` первым), то вывелось бы B, B, B – срабатывает первый подходящий блок except.

Последний блок except может опускать имена исключений, чтобы служить универсальным обработчиком. Используйте это с особой осторожностью, поскольку легко скрыть настоящую ошибку программирования таким образом! Его также можно использовать для вывода сообщения об ошибке и последующего повторного возбуждения исключения (позволяя вызывающему коду также обработать исключение):

```python
import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except OSError as err:
    print("OS error: {0}".format(err))
except ValueError:
    print("Could not convert data to an integer.")
except:
    print("Unexpected error:", sys.exc_info()[0])
    raise
```

Оператор [`try`](https://python-all.ru/3.9/reference/compound_stmts.html#try) … [`except`](https://python-all.ru/3.9/reference/compound_stmts.html#except) имеет необязательную *часть else*, которая, если присутствует, должна следовать за всеми блоками except. Она полезна для кода, который должен быть выполнен, если блок try не вызвал исключение. Например:

```python
for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except OSError:
        print('cannot open', arg)
    else:
        print(arg, 'has', len(f.readlines()), 'lines')
        f.close()
```

Использование блока `else` лучше, чем добавление дополнительного кода в блок [`try`](https://python-all.ru/3.9/reference/compound_stmts.html#try), поскольку это позволяет избежать случайного перехвата исключения, которое не было возбуждено кодом, защищённым инструкцией `try` … `except`.

Когда возникает исключение, оно может иметь связанное значение, также известное как *аргумент* исключения. Наличие и тип аргумента зависят от типа исключения.

Блок except может указывать переменную после имени исключения. Эта переменная связывается с экземпляром исключения, аргументы которого хранятся в `instance.args`. Для удобства экземпляр исключения определяет [`__str__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__str__), так что аргументы можно вывести напрямую без обращения к `.args`. Можно также сначала создать экземпляр исключения, а затем возбудить его и добавить к нему любые атрибуты по желанию.

```python
>>> try:
...     raise Exception('spam', 'eggs')
... except Exception as inst:
...     print(type(inst))    # экземпляр исключения
...     print(inst.args)     # аргументы, хранящиеся в .args
...     print(inst)          # __str__ позволяет выводить args напрямую, но может быть переопределён в подклассах исключений
...                          # но может быть переопределён в подклассах исключений
...     x, y = inst.args     # распаковать args
...     print('x =', x)
...     print('y =', y)
...
<class 'Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs
```

Если исключение имеет аргументы, они выводятся как последняя часть («подробности») сообщения для необработанных исключений.

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

```python
>>> def this_fails():
...     x = 1/0
...
>>> try:
...     this_fails()
... except ZeroDivisionError as err:
...     print('Handling run-time error:', err)
...
Handling run-time error: division by zero
```

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

Инструкция [`raise`](https://python-all.ru/3.9/reference/simple_stmts.html#raise) позволяет программисту принудительно вызвать указанное исключение. Например:

```python
>>> raise NameError('HiThere')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: HiThere
```

Единственный аргумент [`raise`](https://python-all.ru/3.9/reference/simple_stmts.html#raise) указывает, какое исключение должно быть возбуждено. Это должен быть либо экземпляр исключения, либо класс исключения (класс, который наследуется от [`Exception`](https://python-all.ru/3.9/library/exceptions.html#Exception)). Если передается класс исключения, он будет неявно создан вызовом его конструктора без аргументов:

```python
raise ValueError  # сокращение для 'raise ValueError()'
```

Если нужно определить, было ли возбуждено исключение, но обрабатывать его не предполагается, более простая форма инструкции [`raise`](https://python-all.ru/3.9/reference/simple_stmts.html#raise) позволяет повторно возбудить исключение:

```python
>>> try:
...     raise NameError('HiThere')
... except NameError:
...     print('An exception flew by!')
...     raise
...
An exception flew by!
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
NameError: HiThere
```

## 8.5. Цепочка исключений

Оператор [`raise`](https://python-all.ru/3.9/reference/simple_stmts.html#raise) позволяет указать необязательный [`from`](https://python-all.ru/3.9/reference/simple_stmts.html#raise), который включает цепочку исключений. Например:

```python
# exc должен быть экземпляром исключения или None.
raise RuntimeError from exc
```

Это может быть полезно при преобразовании исключений. Например:

```python
>>> def func():
...     raise IOError
...
>>> try:
...     func()
... except IOError as exc:
...     raise RuntimeError('Failed to open database') from exc
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in func
OSError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Failed to open database
```

Цепочка исключений создается автоматически, когда исключение возбуждается внутри секции [`except`](https://python-all.ru/3.9/reference/compound_stmts.html#except) или [`finally`](https://python-all.ru/3.9/reference/compound_stmts.html#finally). Цепочку исключений можно отключить, используя идиому `from None`:

```python
>>> try:
...     open('database.sqlite')
... except OSError:
...     raise RuntimeError from None
...
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError
```

Дополнительную информацию о механизме сцепления см. в разделе [Встроенные исключения](https://python-all.ru/3.9/library/exceptions.html#bltin-exceptions).

## 8.6. Определяемые пользователем исключения

Программы могут задавать собственные исключения, создавая новый класс исключения (см. [Классы](https://python-all.ru/3.9/tutorial/classes.html#tut-classes) для получения дополнительной информации о классах Python). Исключения обычно должны быть производными от класса [`Exception`](https://python-all.ru/3.9/library/exceptions.html#Exception), прямо или косвенно.

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

Большинство исключений имеют имена, оканчивающиеся на «Error», подобно именованию стандартных исключений.

Многие стандартные модули определяют собственные исключения для сообщения об ошибках, которые могут возникнуть в определяемых ими функциях. Дополнительная информация о классах представлена в главе [Классы](https://python-all.ru/3.9/tutorial/classes.html#tut-classes).

## 8.7. Определение действий по очистке

Инструкция [`try`](https://python-all.ru/3.9/reference/compound_stmts.html#try) имеет ещё один необязательный блок, предназначенный для определения действий по очистке, которые должны быть выполнены при любых обстоятельствах. Например:

```python
>>> try:
...     raise KeyboardInterrupt
... finally:
...     print('Goodbye, world!')
...
Goodbye, world!
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
KeyboardInterrupt
```

Если присутствует блок [`finally`](https://python-all.ru/3.9/reference/compound_stmts.html#finally), блок `finally` будет выполнен как последняя задача перед завершением инструкции [`try`](https://python-all.ru/3.9/reference/compound_stmts.html#try). Блок `finally` выполняется независимо от того, вызывает ли инструкция `try` исключение. В следующих пунктах рассматриваются более сложные случаи возникновения исключения:

- Если исключение возникает во время выполнения блока `try`, оно может быть обработано блоком [`except`](https://python-all.ru/3.9/reference/compound_stmts.html#except). Если исключение не обработано блоком `except`, оно повторно возбуждается после выполнения блока `finally`.
- Исключение может возникнуть во время выполнения блока `except` или `else`. В этом случае исключение также повторно возбуждается после выполнения блока `finally`.
- Если в `finally` блоке выполняется оператор [`break`](https://python-all.ru/3.9/reference/simple_stmts.html#break), [`continue`](https://python-all.ru/3.9/reference/simple_stmts.html#continue) или [`return`](https://python-all.ru/3.9/reference/simple_stmts.html#return), исключения не возбуждаются повторно.
- Если инструкция `try` достигает инструкции [`break`](https://python-all.ru/3.9/reference/simple_stmts.html#break), [`continue`](https://python-all.ru/3.9/reference/simple_stmts.html#continue) или [`return`](https://python-all.ru/3.9/reference/simple_stmts.html#return), блок `finally` выполняется непосредственно перед выполнением инструкции `break`, `continue` или `return`.
- Если в `finally` блоке содержится оператор `return`, возвращаемое значение будет взято из оператора `return` блока `finally`, а не из оператора `return` блока `try`.

Например:

```python
>>> def bool_return():
...     try:
...         return True
...     finally:
...         return False
...
>>> bool_return()
False
```

Более сложный пример:

```python
>>> def divide(x, y):
...     try:
...         result = x / y
...     except ZeroDivisionError:
...         print("division by zero!")
...     else:
...         print("result is", result)
...     finally:
...         print("executing finally clause")
...
>>> divide(2, 1)
result is 2.0
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'
```

Как видите, блок [`finally`](https://python-all.ru/3.9/reference/compound_stmts.html#finally) выполняется в любом случае. Исключение [`TypeError`](https://python-all.ru/3.9/library/exceptions.html#TypeError), возбуждённое при делении двух строк, не обрабатывается блоком [`except`](https://python-all.ru/3.9/reference/compound_stmts.html#except) и поэтому повторно возбуждается после выполнения блока `finally`.

В реальных приложениях блок [`finally`](https://python-all.ru/3.9/reference/compound_stmts.html#finally) полезен для освобождения внешних ресурсов (таких как файлы или сетевые соединения) независимо от того, было ли использование ресурса успешным.

## 8.8. Предопределённые действия по очистке

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

```python
for line in open("myfile.txt"):
    print(line, end="")
```

Проблема этого кода в том, что файл остаётся открытым в течение неопределённого времени после завершения выполнения этой части кода. В простых скриптах это не проблема, но может стать проблемой для более крупных приложений. Инструкция [`with`](https://python-all.ru/3.9/reference/compound_stmts.html#with) позволяет использовать такие объекты, как файлы, таким образом, чтобы гарантировать их своевременную и корректную очистку.

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

После выполнения инструкции файл *f* всегда закрывается, даже если при обработке строк возникла проблема. Объекты, которые, подобно файлам, предоставляют предопределённые действия по очистке, сообщают об этом в своей документации.
