Рсключения ========== Рсключения возникают тогда, РєРѕРіРґР° РІ программе возникает некоторая *исключительная* ситуация. Например, Рє чему приведёт попытка чтения несуществующего файла? Рли если файл был случайно удалён, РїРѕРєР° программа работала? Такие ситуации обрабатываются РїСЂРё помощи **исключений**. Рто касается Рё программ, содержащих недействительные команды. Р’ этом случае Python **поднимает** СЂСѓРєРё Рё сообщает, что обнаружил **ошибку**. Ошибки ------ Рассмотрим простой вызов функции ``print``. Что, если РјС‹ ошибочно напишем ``print`` как ``Print``? Обратите внимание РЅР° заглавную Р±СѓРєРІСѓ. Р’ этом случае Python *поднимает* синтаксическую ошибку. .. sourcecode:: 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) Рё посмотрите, что произойдёт. .. sourcecode:: 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``\ [1]_. РџСЂРё этом РІСЃРµ обычные команды помещаются внутрь try-блока, Р° РІСЃРµ обработчики исключений -- РІ except-блок. **Пример:** (сохраните как ``try_except.py``) .. sourcecode:: python try: text = input('Введите что-РЅРёР±СѓРґСЊ --> ') except EOFError: print('РќСѓ зачем РІС‹ сделали РјРЅРµ EOF?') except KeyboardInterrupt: print('Р’С‹ отменили операцию.') else: print('Р’С‹ ввели {0}'.format(text)) **Вывод:** :: $ 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``\ [2]_, передав ему РёРјСЏ ошибки/исключения, Р° также объект исключения, который нужно *выбросить*. Вызываемая ошибка или исключение должна быть классом, который РїСЂСЏРјРѕ или непрямо является производным РѕС‚ класса ``Exception``. **Пример:** (сохраните как ``raising.py``) .. sourcecode:: 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('РќРµ было исключений.') **Вывод:** :: $ python3 raising.py Введите что-РЅРёР±СѓРґСЊ --> Р° ShortInputException: Длина введённой строки -- 1; ожидалось, как РјРёРЅРёРјСѓРј, 3 $ python3 raising.py Введите что-РЅРёР±СѓРґСЊ --> абв РќРµ было исключений. **Как это работает:** Здесь РјС‹ создаём наш собственный тип исключения. Ртот новый тип исключения называется ``ShortInputException``. РћРЅ содержит РґРІР° поля: ``length``, хранящее длину введённого текста, Рё ``atleast``, указывающее, какую минимальную длину текста ожидала программа. Р’ пункте ``except`` РјС‹ указываем класс ошибки ``ShortInputException``, который будет сохранён как\ [3]_ переменная ``ex``, содержащая соответствующий объект ошибки/исключения. Рто аналогично параметрам Рё аргументам РїСЂРё вызове функции. Внутри этого пункта ``except`` РјС‹ используем поля ``length`` Рё ``atleast`` объекта исключения для вывода необходимых сообщений пользователю. Try .. Finally -------------- Представим, что РІ программе РїСЂРѕРёСЃС…РѕРґРёС‚ чтение файла Рё необходимо убедиться, что объект файла был корректно закрыт Рё что РЅРµ возникло никакого исключения. Ртого можно достичь СЃ применением блока ``finally``. Сохраните как ``finally.py``: .. sourcecode:: 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('(Очистка: Закрытие файла)') **Вывод:** :: $ python3 finally.py Программировать весело Если работа скучна, Чтобы придать ей весёлый тон - !! Р’С‹ отменили чтение файла. (Очистка: Закрытие файла) **Как это работает:** Здесь РјС‹ РїСЂРѕРёР·РІРѕРґРёРј обычные операции чтения РёР· файла, РЅРѕ РІ данном случае добавляем двухсекундный СЃРѕРЅ после вывода каждой строки РїСЂРё помощи функции ``time.sleep``, чтобы программа выполнялась медленно (ведь Python очень быстр РѕС‚ РїСЂРёСЂРѕРґС‹). Р’Рѕ время выполнения программы нажмите ``ctrl-c``, чтобы прервать/отменить выполнение программы. Пронаблюдайте, как РїСЂРё этом выдаётся исключение ``KeyboardInterrupt``, Рё программа выходит. Однако, прежде чем программа выйдет, выполняется РїСѓРЅРєС‚ ``finally``, Рё файловый объект будет всегда закрыт. Оператор with ------------- Типичной схемой является запрос некоторого ресурса РІ блоке ``try`` СЃ последующим освобождением этого ресурса РІ блоке ``finally``. Для того, чтобы сделать это более "чисто", существует оператор ``with``\ [4]_: Сохраните как ``using_with.py``: .. sourcecode:: 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`. Резюме ------ РњС‹ обсудили использование операторов ``try..except`` Рё ``try..finally``. РњС‹ также увидели, как создавать наши собственные типы исключений Рё как РёС… вызывать. Далее РјС‹ ознакомимся СЃРѕ стандартной библиотекой Python. Примечания ---------- .. [1] try -- *англ.* "пытаться" (*РїСЂРёРј.перев.*) .. [2] raise -- *англ.* "поднимать" (*РїСЂРёРј.перев.*) .. [3] as -- *англ.* "как" (*РїСЂРёРј.перев.*) .. [4] with -- *англ.* "СЃ" (*РїСЂРёРј.перев.*)