Ошибки и исключения в Python

Все ошибки в языке Python делятся на два типа:
  • синтаксические ошибки;
  • исключения - ошибки, которые возникают в процессе исполнения кода.

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

Ошибки также являются объектами и поэтому у любой ошибки есть тип:
  • IndexError - мы пытались взять индекс, которого нет в списке.
  • NameError - это такая ошибка, которая встречается, когда мы не можем найти имя в пространстве имен.
  • TypeError - мы вероятно передали в какую-нибудь функцию, что-нибудь неправильного типа.

Ошибка хранит в себе состояние стека вызова на тот момент, когда совершилась ошибка.

Как ловить (catch) исключения

Некоторую часть ошибок можно поймать и обработать:
try:
    x = [1, 2, "hello", 7]
    x.sort()
    print(x)
except TypeError:
    print("Type error :(")

print("I can catch")

Внутри блока try на самом деле генерируется объект исключения. У этого объекта нет имени, однако где-то у нас есть такой объект e, его не существует ни в каком неймспейсе.
try:
    15 / 0
    # e
except ZeroDivisionError:  # isinstance(e, ZeroDivisionError) == True
    print("Division by zero")
Мы используем isinstance, а не сравнение типов через type потому что все ошибки в языке Python представляют собой иерархиюZeroDivisionError наследуется от класса ArithmeticError, т.е. он является арифметической ошибкой:
>>> print(ZeroDivisionError.mro())
[<class 'ZeroDivisionError'>, <class 'ArithmeticError'>, <class 'Exception'>, <class 'BaseException'>, <class 'object'>]
Из-за использования функции isinstance никогда не имеет смысла проверять наследника внутри одного except-блока после предка в except-блоке выше:
try:
    15 / 0
    # e
except ZeroDivisionError:  # isinstance(e, ZeroDivisionError) == True
    print("Division by zero")
except ZeroDivisionError:  # не имеет смысла, т.к. все арифметические ошибки мы поймали выше
    print("zero division")

print(ZeroDivisionError.mro())

Когда мы пишем несколько except блоков важно помнить, что любое исключение будет обработано лишь одним из них - первым под которое оно подойдет.
def f(x,y):
    try:
        return x / y
    except TypeError:
        print("Type error")
    except ZeroDivisionError:
        print("Zero division :(")

f(5, "123")
print(f(5,0))

Мы можем ловить наши исключения в любой момент исполнения на стеке:
def f(x,y):
    try:
        return x / y
    except TypeError:
        print("Type error")

f(5, "123")

try:
    print(f(5,0))
except ZeroDivisionError:
    print("Zero division :(")

Одним except-блоком можно поймать сразу несколько типов исключений:
def f(x,y):
    try:
        return x / y
    except (TypeError, ZeroDivisionError):
        print("Error :(")

print(f(5,0))
print(f(5,[]))

Также можно поймать и сам объект ошибки:
def f(x,y):
    try:
        return x / y
    except (TypeError, ZeroDivisionError) as e:
        print(type(e))
        print(e)
        print(e.args)

print(f(5,0))
print(f(5,[]))

Можно исполнить try-except-блок без указания конкретных типов исключений, которые можно ловить:
def f(x,y):
    try:
        return x / y
    except:  # поймает любую ошибку
        print("Error :(")

print(f(5,0))
print(f(5,[]))

Ключевое слово else используется, когда внутри try-блока не возникло никакого исключения и мы возможно хотим что-то сделать в этом случае. А блок finally запускается в любом случае.
def divide(x, y):
    try:
        result = x / y
    except ZeroDivisionError:
        print("division by zero")
    else:  # если не будет никакого исключения
        print("result is", result)
    finally:  # просто выведем строку "finally"
        print("finally")

divide(2, 1)
divide(2, 0)
divide(2, [])

Как бросать (throw) исключения

Для того чтобы бросить исключение в языке Python необходимо использовать конструкцию rise, в которую нужно передать объект нашего исключения.
def greet(name):
    if name[0].isupper():
        return "Hello, " + name
    else:
        raise ValueError(name + " is inappropriate name")

print(greet("Abdullah"))
print(greet("abdullah"))  # а неприличные люди получат ошибку

Все исключения, которые мы ловим с помощью except и бросаем с помощью raise должны быть экземплярами BaseException.

Можно написать свой собственный класс исключений:
class BadName(Exception):
    pass

def greet(name):
    if name[0].isupper():
        return "Hello, " + name
    else:
        raise BadName(name + " is inappropriate name")

while True:
    try:
        name = input("Please enter your name: ")
        greeting = greet(name)
        print(greeting)
    except ValueError:
        print("Please try again")
    else:
        break

Комментариев нет:

Отправить комментарий