Исключения
Исключения возникают тогда, когда в программе возникает некоторая исключительная ситуация. Например, к чему приведёт попытка чтения несуществующего файла? Или если файл был случайно удалён, пока программа работала? Такие ситуации обрабатываются при помощи исключений.
Это касается и программ, содержащих недействительные команды. В этом случае Python поднимает руки и сообщает, что обнаружил ошибку.
Ошибки
Рассмотрим простой вызов функции print
. Что, если мы ошибочно напишем print
как Print
? Обратите внимание на заглавную букву. В этом случае Python поднимает синтаксическую ошибку.
>>> Print("Привет, Мир")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'Print' is not defined
>>> print("Привет, Мир!")
Привет, Мир!
Обратите внимание, что была поднята ошибка NameError
, а также указано место, где была обнаружена ошибка. Так в данном случае действует обработчик ошибок.
Исключения
Попытаемся считать что-либо от пользователя. Введите первую строку ниже и нажмите клавишу Enter
. Когда компьютер попросит вас ввести текст, вместо этого нажмите [ctrl-d]
на Mac или [ctrl-z]
в Windows и посмотрите, что произойдет. (Если вы используете Windows и ни один из вариантов не работает, вы можете попробовать [ctrl-c]
в Командной строке, чтобы вместо этого сгенерировать ошибку KeyboardInterrupt
).
>>> s = input('Введите что-нибудь --> ')
Введите что-нибудь --> Traceback (most recent call last):
File "<stdin>", line 1, in <module>
EOFError
Python поднимает ошибку с именем EOFError
, что означает, что он обнаружил символ конца файла (который вводится при помощи ctrl-d
) там, где не ожидал.
Обработка исключений
Обрабатывать исключения можно при помощи оператора try..except
1. При этом все обычные команды помещаются внутрь try-блока, а все обработчики исключений – в except-блок.
Пример (сохраните как exceptions_handle.py
):
try:
text = input('Введите что-нибудь --> ')
except EOFError:
print('Ну зачем вы сделали мне EOF?')
except KeyboardInterrupt:
print('Вы отменили операцию.')
else:
print('Вы ввели {}'.format(text))
Вывод:
# Press ctrl + d
$ python exceptions_handle.py
Введите что-нибудь --> Ну зачем вы сделали мне EOF?
# Press ctrl + c
$ python exceptions_handle.py
Введите что-нибудь --> ^CВы отменили операцию.
$ python exceptions_handle.py
Введите что-нибудь --> Без ошибок
Вы ввели Без ошибок
Как это работает
Здесь мы поместили все команды, которые могут вызвать исключения/ошибки, внутрь блока try
, а затем поместили обработчики соответствующих ошибок/исключений в блок except
. Выражение except
может обрабатывать как одиночную ошибку или исключение, так и список ошибок/исключений в скобках. Если не указано имя ошибки или исключения, обрабатываться будут все ошибки и исключения.
Помните, что для каждого выражения try
должно быть хотя бы одно соответствующее выражение except
.Иначе какой смысл был бы в блоке try?
Если ошибка или исключение не обработано, будет вызван обработчик Python по умолчанию, который останавливает выполнение программы и выводит на экран сообщение об ошибке. Выше мы уже видели это в действии.
Можно также добавить пункт else
к соответствующему блоку try..except
. Этот пункт будет выполнен тогда, когда исключений не возникает.
В следующем примере мы увидим, как можно получить объект исключения для дальнейшей работы с ним.
Вызов исключения
Исключение можно поднять при помощи оператора raise
2, передав ему имя ошибки/исключения, а также объект исключения, который нужно выбросить.
Вызываемая ошибка или исключение должна быть классом, который прямо или непрямо является производным от класса Exception
.
Пример (сохраните как exceptions_raise.py
):
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 exceptions_raise.py
Введите что-нибудь --> а
ShortInputException: Длина введённой строки 1, ожидалось, как минимум 3
$ python exceptions_raise.py
Введите что-нибудь --> абв
Не было исключений.
Как это работает
Здесь мы создаём наш собственный тип исключения. Этот новый тип исключения называется ShortInputException
. Он содержит два поля: length
, хранящее длину введённого текста, и atleast
, указывающее, какую минимальную длину текста ожидала программа.
В пункте except
мы указываем класс ошибки ShortInputException
, который будет сохранён как
3 переменная ex
, содержащая соответствующий объект ошибки/исключения. Это аналогично параметрам и аргументам при вызове функции. Внутри этого пункта except
мы используем поля length
и atleast
объекта исключения для вывода необходимых сообщений пользователю.
Try ... Finally
Представим, что в программе происходит чтение файла и необходимо убедиться, что объект файла был корректно закрыт и что не возникло никакого исключения. Этого можно достичь с применением блока finally.
Сохраните как exceptions_finally.py
:
import sys
import time
f = None
try:
f = open("poem.txt")
# Наш обычный способ читать файлы
while True:
line = f.readline()
if len(line) == 0:
break
print(line, end='')
sys.stdout.flush()
print("Нажмите ctrl+c сейчас")
# Пусть подождёт некоторое время
time.sleep(2)
except IOError:
print("Не удалось найти файл poem.txt")
except KeyboardInterrupt:
print("!! Вы отменили чтение файла.")
finally:
if f:
f.close()
print("(Очистка: Закрытие файла)")
Вывод:
$ python exceptions_finally.py
Программировать весело
Нажмите ctrl+c сейчас
^C!! Вы отменили чтение файла.
(Очистка: Закрытие файла)
Как это работает
Здесь мы производим обычные операции чтения из файла, но в данном случае добавляем двухсекундный сон после вывода каждой строки при помощи функции time.sleep
, чтобы программа выполнялась медленно (ведь Python очень быстр от природы). Во время выполнения программы нажмите ctrl + c
, чтобы прервать/отменить выполнение программы.
Пронаблюдайте, как при этом выдаётся исключение KeyboardInterrupt
, и программа выходит. Однако, прежде чем программа выйдет, выполняется пункт finally
, и файловый объект будет всегда закрыт.
Обратите внимание, что переменная, которой присвоено значение 0 или None
, или переменная, которая является пустой последовательностью или коллекцией, рассматривается Python как False
. Вот почему мы можем использовать if f:
в приведенном выше коде.
Также обратите внимание, что мы используем sys.stdout.flush()
после print
, чтобы сразу вывести на экран.
Оператор with
Типичной схемой является запрос некоторого ресурса в блоке try
с последующим освобождением этого ресурса в блоке finally
. Для того, чтобы сделать это более "чисто", существует оператор with
4:
Сохраните как exceptions_using_with.py
:
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.
Резюме
Мы обсудили использование операторов try..except
и try..finally
. Мы также увидели, как создавать наши собственные типы исключений и как их вызывать.
Далее мы ознакомимся со стандартной библиотекой Python.
Примечания
1. try – англ. "пытаться" (прим.перев.) ↩
2. raise – англ. "поднимать" (прим.перев.) ↩
3. as – англ. "как" (прим.перев.) ↩
4. with – англ. "с" (прим.перев.) ↩