Исключения

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

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

 def even_the_odds(odds):
    if odds % 2 != 1:
        raise ValueError("Did not get an odd number")

    return odds + 1 

Ловить исключения

Используйте try...except: перехватывать исключения. Вы должны указать как можно более точное исключение:

 try:
    x = 5 / 0
except ZeroDivisionError as e:
    # `e` is the exception object
    print("Got a divide by zero! The exception was:", e)
    # handle exceptional case
    x = 0  
finally:
    print "The END"
    # it runs no matter what execute.

 

Класс исключения , который указан - в данном случае, ZeroDivisionError - перехватывает любое исключение, которое из этого класса или любого подкласса этого исключения.

Например, ZeroDivisionError подкласс ArithmeticError :

 >>> ZeroDivisionError.__bases__
(<class 'ArithmeticError'>,)

 

Итак, следующий будет еще поймать ZeroDivisionError :

 try:
    5 / 0
except ArithmeticError:
    print("Got arithmetic error") 

Запуск кода очистки с помощью наконец

Иногда вы можете захотеть, чтобы что-то происходило независимо от того, какое исключение произошло, например, если вам нужно очистить некоторые ресурсы.

, finally , блок из try пункта будет происходить независимо от того, были ли какие - либо исключения подняли.

 resource = allocate_some_expensive_resource()
try:
    do_stuff(resource)
except SomeException as e:
    log_error(e)
    raise  # re-raise the error
finally:
    free_expensive_resource(resource)

 

Эта модель часто лучше обращаться с менеджерами контекста ( с использованием с утверждением ).

Возрождение исключений

Иногда вы хотите поймать исключение только для его проверки, например, в целях регистрации. После проверки вы хотите, чтобы исключение продолжало распространяться, как и раньше.

В этом случае, просто используйте raise заявление без каких - либо параметров.

 try:
    5 / 0
except ZeroDivisionError:
    print("Got an error")
    raise

 

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

 try:
    5 / 0
except ZeroDivisionError as e:
    raise ZeroDivisionError("Got an error", e)

 

Но это имеет недостаток , заключающийся в сокращении следа исключения в точности этого raise в то время как raise без аргумента сохраняет оригинальный след исключения.

В Python 3 вы можете сохранить первоначальный стек с помощью raise - from синтаксисом:

     raise ZeroDivisionError("Got an error") from e 

Цепные исключения с повышением от

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

Вы можете объединить исключения, чтобы показать, как происходит обработка исключений: `` `>>> try: 5/0 за исключением ZeroDivisionError как e: повысить ValueError (" Ошибка деления ") из e Traceback (последний вызов был последним): File" ", строка 2, в  ZeroDivisionError: деление на ноль Вышеуказанное исключение было прямой причиной следующего исключения: обратная связь (последний вызов был последним): файл " ", строка 4, в  ValueError: Разделение не удалось `` `

Иерархия исключений

Обработка исключений происходит на основе иерархии исключений, определяемой структурой наследования классов исключений.

Например, IOError и OSError являются подклассами EnvironmentError.Код , который ловит IOError не поймать OSError.Тем не менее, код , который ловит EnvironmentError будет ловить как IOError s и OSError s.

Иерархия встроенных исключений:

`` `BaseException + - SystemExit + - KeyboardInterrupt + - GeneratorExit + - Exception + - StopIteration + - StandardError | + - BufferError | + - ArithmeticError | | + - FloatingPointError | | + - OverflowError | | + - ZeroDivisionError | + - AssertionError | + - AttributeError | + - EnvironmentError | | + - IOError | | + - OSError | | + - WindowsError (Windows) | | + - VMSError (VMS) | + - EOFError | + - ImportError | + - LookupError | | + - IndexError | | + - KeyError | + - MemoryError | + - NameError | | + - UnboundLocalError | + - ReferenceError | + - RuntimeError | | + - NotImplementedError | + - SyntaxError | | + - IndentationError | | + - TabError | + - SystemError | + - TypeError | + - ValueError | + - UnicodeError | + - UnicodeDecodeError | + - UnicodeEncodeError | + - UnicodeTranslateError + - Предупреждение + - Предупреждение об устаревании + - Предупреждение об ожидании + - Время выполнения + - Синтаксическое предупреждение + - Предупреждение пользователя + - FutureWarning + - ImportWarning + - UnicodeWarning + - BytesWarning `` `   `` `BaseException + - SystemExit + - KeyboardInterrupt + - GeneratorExit + - Exception + - StopIeror + - StopAsyncIteration + - ArithmeticError | + - FloatingPointError | + - OverflowError | + - ZeroDivisionError + - AssertionError + - AttributeError + - BufferError + - EOFError + - ImportError + - LookupError | + - IndexError | + - KeyError + - MemoryError + - NameError | + - UnboundLocalError + - OSError | + - BlockingIOError | + - ChildProcessError | + - ConnectionError | | + - BrokenPipeError | | + - ConnectionAbortedError | | + - ConnectionRefusedError | | + - ConnectionResetError | + - FileExistsError | + - FileNotFoundError | + - InterruptedError | + - IsADirectoryError | + - NotADirectoryError | + - PermissionError | + - ProcessLookupError | + - TimeoutError + - ReferenceError + - RuntimeError | + - NotImplementedError | + - RecursionError + - SyntaxError | + - IndentationError | + - TabError + - SystemError + - TypeError + - ValueError | + - UnicodeError | + - UnicodeDecodeError | + - UnicodeEncodeError | + - UnicodeTranslateError + - Предупреждение + - Предупреждение об устаревании + - Предупреждение об ожидании + - Время выполнения + - Синтаксическое предупреждение + - Предупреждение пользователя + - FutureWarning + - ImportWarning + - UnicodeWarning + - BytesWarning + - ResourceWarning `` `

Исключения тоже объекты

Исключением являются только обычные объекты Python , которые наследуют от встроенного BaseException.Сценарий Python может использовать raise заявление , чтобы прервать выполнение, в результате чего Python для печати трассировки стеки из стека вызовов в этой точке и представление экземпляра исключения. Например:

 >>> def failing_function():
...     raise ValueError('Example error!')
>>> failing_function()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in failing_function
ValueError: Example error! 

который говорит , что ValueError с сообщением 'Example error!' был поднят нашей failing_function() , который был выполнен в интерпретаторе.

Код вызова может выбрать обработку любых исключений, которые может вызвать вызов:

 >>> try:
...     failing_function()
... except ValueError:
...     print('Handled the error')
Handled the error 

Вы можете разжиться объектов исключения, назначая их в except... часть кода обработки исключений:

 >>> try:
...     failing_function()
... except ValueError as e:
...     print('Caught exception', repr(e))
Caught exception ValueError('Example error!',) 

Полный список встроенных исключений Python вместе с их описаниями можно найти в документации по Python: https://docs.python.org/3.5/library/exceptions.html.А вот полный список организованы иерархически: https://codecamp.ru/documentation/python/1788/exceptions/5535/exception-hierarchy .

Создание пользовательских типов исключений

Создайте класс , унаследованный от Exception :

 class FooException(Exception):
    pass
try:
    raise FooException("insert description here")
except FooException:
    print("A FooException was raised.")

 

или другой тип исключения:

 class NegativeError(ValueError):
      pass

def foo(x):
    # function that only accepts positive values of x
    if x < 0:
        raise NegativeError("Cannot process negative numbers")
    ...  # rest of function body
try:
    result = foo(int(input("Enter a positive integer: ")))  # raw_input in Python 2.x
except NegativeError:
    print("You entered a negative number!")
else:
    print("The result was " + str(result)) 

Не лови все!

В то время как это часто заманчивым , чтобы поймать каждый Exception :

 try:
    very_difficult_function()
except Exception:
    # log / try to reconnect / exit gratiously
finally:
    print "The END"
    # it runs no matter what execute.

 

Или даже все (что включает в себя BaseException и всех его детей , включая Exception ):

 try:
    even_more_difficult_function()
except:
    pass  # do whatever needed

 

В большинстве случаев это плохая практика. Это может поймать больше , чем предполагалось, например, SystemExit , KeyboardInterrupt и MemoryError - каждый из которых обычно следует обращаться иначе , чем обычные системы или логические ошибки. Это также означает, что нет четкого понимания того, что внутренний код может делать неправильно и как правильно восстанавливаться после этого состояния. Если вы ловите каждую ошибку, вы не будете знать, какая ошибка произошла или как ее исправить.

Это чаще называют «маскированием ошибок» и его следует избегать. Пусть ваша программа аварийно завершает работу, а не молча терпит неудачу или, что еще хуже, терпит неудачу на более глубоком уровне исполнения (Представьте, что это транзакционная система)

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

Поймать несколько исключений

Есть несколько путей , чтобы поймать несколько исключений ,

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

 try:
    d = {}
    a = d[1]
    b = d.non_existing_field
except (KeyError, AttributeError) as e:
    print("A KeyError or an AttributeError exception has been caught.")

 

Если вы хотите обрабатывать разные исключения по-разному, вы можете предоставить отдельный блок исключений для каждого типа. В этом примере, мы до сих пор поймать KeyError и AttributeError , но обрабатывать исключения по - разному.

 try:
    d = {}
    a = d[1]
    b = d.non_existing_field
except KeyError as e:
    print("A KeyError has occurred. Exception message:", e)
except AttributeError as e:
    print("An AttributeError has occurred. Exception message:", e)

 

Практические примеры обработки исключений

Пользовательский ввод

Представьте , что вы хотите, чтобы пользователь , чтобы ввести номер через input.Вы хотите убедиться, что ввод является числом. Вы можете использовать try / за except для этого:

в то время как True: try: nb = int (input('Введите число:')) break, кроме ValueError: print("Это не число, попробуйте еще раз.")

Примечание: Python 2.x будет использовать raw_input вместо; функция input существует в Python 2.x , но имеет различную семантику. В приведенном выше примере, input также будет принимать такие выражения, как 2 + 2 , которая является числом.

Если входные данные не могут быть преобразованы в целое число, ValueError поднимается. Вы можете поймать его с except.Если исключение не возникает, break выскакивает из цикла. После цикла, nb содержит целое число.

Словари

Представьте , что вы итерация список последовательных целых чисел, как range(n) , и у вас есть список словарей d , который содержит информацию о том, что можно сделать , когда вы столкнетесь с некоторыми особыми целыми числами, скажем , пропустить d[i] последующие.

 d = [{7: 3}, {25: 9}, {38: 5}]

for i in range(len(d)):
    do_stuff(i)
    try:
       dic = d[i]
       i += dic[i]
    except KeyError:
       i += 1

 

KeyError будет поднят при попытке получить значение из словаря для ключа , который не существует.

еще

Код в блоке еще будет работать , только если никакие исключения не были подняты кодом в try блоке. Это полезно, если у вас есть какой-то код, который вы не хотите запускать, если выдается исключение, но вы не хотите, чтобы исключения, генерируемые этим кодом, были перехвачены.

Например:

 try:
    data = {1: 'one', 2: 'two'}
    print(data[1])
except KeyError as e:
    print('key not found')
else:
    raise ValueError()
# Output: one
# Output: ValueError

 

Обратите внимание , что этот вид else: не может быть объединен с if запуске другого придаточного к elif.Если у вас есть следующее , if это необходимо , чтобы остаться с отступом ниже, else: :

 try:
    ...
except ...:
    ...
else:
    if ...:
        ...
    elif ...:
        ...
    else:
        ...