Синтаксические ошибки и встроенные исключения в Python

Обновлено:

Мы уже знаем, что проблемы в программе могут возникать на разных этапах.

На этом уроке познакомимся с конкретными ошибками и исключениями, которые уже могли встретиться в наших программах:

ZeroDivisionError
NameError
UnboundLocalError
SyntaxError
IndentationError
TypeError
ValueError

Их названия находятся во встроенной области видимости Python, поэтому к ним можно обращаться напрямую:

print(ZeroDivisionError)
print(TypeError)
print(ValueError)

Это классы встроенных исключений Python. В дальнейшем мы научимся обрабатывать исключения и создавать собственные классы исключений на основе уже существующих.


1. ZeroDivisionError

ZeroDivisionError возникает при попытке выполнить деление или получить остаток от деления на ноль.

Самый простой пример:

result = 10 / 0

Программа завершится исключением:

ZeroDivisionError: division by zero

Такая же проблема возникает с другими операциями деления:

10 // 0
10 % 0

ZeroDivisionError внутри функции

Иногда ноль появляется не непосредственно в коде, а становится результатом вычислений:

def calculate_average(total, count, removed):
    actual_count = count - removed

    return total / actual_count


print(calculate_average(100, 5, 5))

Внутри функции:

actual_count = 5 - 5

получаем:

0

После этого выполняется:

100 / 0

и возникает ZeroDivisionError.


Проверка перед делением

Мы уже знаем условные конструкции, поэтому можем заранее проверить делитель:

def calculate_average(total, count, removed):
    actual_count = count - removed

    if actual_count == 0:
        return None

    return total / actual_count

Теперь:

print(calculate_average(100, 5, 5))

Результат:

None

Мы заранее предусмотрели ситуацию, в которой деление невозможно, и не допустили возникновения исключения.

Позже мы познакомимся со специальным механизмом обработки исключений.


2. NameError

NameError возникает, когда Python пытается найти имя в доступных областях видимости, но такого имени не существует.

Например:

print(product_price)

Если product_price нигде не определено:

NameError

Обращение раньше определения

print(product_price)

product_price = 1500

Python выполняет программу сверху вниз.

В момент выполнения:

print(product_price)

имя ещё не существует, поэтому возникает NameError.


Опечатка в имени

product_price = 1500

print(prodcut_price)

Переменная называется:

product_price

а обращаемся мы к:

prodcut_price

Для Python это два разных имени.


Регистр тоже имеет значение

product_price = 1500

print(Product_price)

Имена:

product_price
Product_price

разные, поэтому тоже получим NameError.


3. UnboundLocalError

С UnboundLocalError мы уже познакомились при изучении областей видимости.

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

UnboundLocalError является подклассом NameError.

Можно посмотреть цепочку наследования:

print(UnboundLocalError.mro())

Обращение до локального присваивания

value = 100


def show_value():
    print(value)

    value = 50


show_value()

Возникнет:

UnboundLocalError

Поскольку внутри функции есть:

value = 50

Python рассматривает value как локальное имя этой функции.

Но:

print(value)

пытается получить его значение раньше, чем оно было присвоено.


Изменение через +=

counter = 10


def increase_counter():
    counter += 1

    return counter


increase_counter()

Здесь операция:

counter += 1

сначала должна получить текущее значение локального counter, а затем присвоить ему новое значение.

Но локальному counter ещё ничего не было присвоено, поэтому возникает:

UnboundLocalError

4. SyntaxError

SyntaxError возникает, когда Python не может разобрать код из-за нарушения синтаксиса языка. Такая ошибка обнаруживается ещё до обычного выполнения соответствующего кода.

Например, забыли закрыть скобку:

print('Hello'

Результат:

SyntaxError

Лишняя скобка

print('Hello'))

Получим:

SyntaxError

Пропущено двоеточие

if 10 > 5
    print('ok')

После условия должно находиться:

:

Без него код синтаксически неправильный.


Неправильное расположение аргументов

Мы уже знаем, что обычный позиционный аргумент нельзя ставить после именованного:

def create_report(name, age):
    return f'{name}: {age}'


create_report(
    name='Alex',
    25,
)

Этот код нельзя корректно разобрать, поэтому возникает SyntaxError.


Как читать SyntaxError

Python обычно показывает:

  • имя файла;
  • номер строки;
  • участок исходного кода;
  • место, возле которого была обнаружена проблема;
  • описание синтаксической ошибки.

Например:

File ".../main.py", line 1
    print('Hello'
         ^
SyntaxError: '(' was never closed

5. IndentationError

В Python отступы являются частью синтаксиса.

IndentationError возникает при неправильном использовании отступов и является подклассом SyntaxError.


Нет необходимого отступа

def create_message():
return 'Hello'

После определения функции её тело должно находиться с отступом:

def create_message():
    return 'Hello'

Неожиданный отступ

product_price = 1500

    print(product_price)

Здесь отступ появился в месте, где Python не ожидает нового блока кода.

Это тоже может привести к:

IndentationError

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


6. TypeError

TypeError возникает, когда операция или функция применяется к объекту неподходящего типа.

Упрощённо:

Python получил не тот тип данных, с которым эта операция умеет работать.


Несовместимые типы при сложении

result = '10' + 5

Слева находится:

str

справа:

int

Операция сложения в таком виде для этих объектов не определена, поэтому возникает:

TypeError

Например:

TypeError: can only concatenate str (not "int") to str

Несовместимые типы при сравнении

result = '10' > 5

Python не выполняет такое сравнение порядка между str и int.

Получим:

TypeError

Если нам действительно нужно сравнить числа, сначала необходимо привести данные к подходящему типу:

result = int('10') > 5

print(result)

Результат:

True

Неподходящий аргумент для int()

Мы можем преобразовать строку с целым числом:

number = int('10')

print(number)

Результат:

10

Но список:

int([10])

не является подходящим типом аргумента для такого преобразования.

Возникнет:

TypeError

Это хороший пример правила:

Операция получила объект неподходящего типа.


Неподходящий объект для max()

Функции:

max()

можно передать итерируемый объект:

max([10, 20, 30])

Результат:

30

Но:

max(10)

приведёт к TypeError, потому что один объект int не является итерируемым объектом.

В сообщении можно увидеть:

TypeError: 'int' object is not iterable

7. ValueError

ValueError возникает, когда тип аргумента подходит операции или функции, но само значение является недопустимым для этой операции.

Это важное отличие от TypeError.

Упрощённо:

TypeError  → неподходящий тип
ValueError → тип подходит, но значение не подходит

int() и правильное значение

number = int('150')

print(number)

Строка подходит для преобразования и содержит корректную запись целого числа.

Результат:

150

int() и неподходящее значение

Теперь передадим тоже строку:

number = int('Python')

Тип аргумента:

str

подходит для int().

Но значение:

Python

невозможно интерпретировать как целое число.

Поэтому возникает:

ValueError

Например:

ValueError: invalid literal for int() with base 10: 'Python'

TypeError и ValueError на одном примере

8. Сравним два вызова int()

int([10])

Получаем:

TypeError

Потому что передан объект неподходящего типа:

list

А:

int('Python')

даёт:

ValueError

Потому что тип:

str

подходит, но конкретное значение строки нельзя преобразовать в целое число.

Это один из самых удобных примеров для понимания разницы между двумя исключениями.


ValueError в max()

Список является подходящим итерируемым объектом:

max([10, 20, 30])

Но пустой список:

max([])

не содержит ни одного элемента, который можно было бы вернуть как максимальный.

Поэтому возникает:

ValueError: max() iterable argument is empty

Здесь тип аргумента правильный:

list

но его значение — пустой список — не подходит для этого варианта вызова max().


9. Главное отличие TypeError и ValueError

Рассмотрим:

int([10])

Вопрос:

Умеет ли int() выполнять такое преобразование из объекта этого типа?

Нет.

Получаем:

TypeError

Теперь:

int('Python')

Вопрос:

Умеет ли int() принимать строки?

Да.

Но:

Можно ли именно эту строку преобразовать в целое число?

Нет.

Получаем:

ValueError

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


10. Синтаксические ошибки и исключения

Важно разделять две ситуации.

Синтаксическая ошибка

Например:

print('Hello'

Python не может нормально разобрать программу:

SyntaxError

IndentationError также относится к синтаксическим ошибкам.

Исключение во время выполнения

Например:

10 / 0

Код синтаксически правильный.

Программа начинает выполняться, но во время выполнения операции возникает:

ZeroDivisionError

Если исключение не обработано, Python показывает traceback и обычное выполнение программы прекращается.


11. Краткая таблица

Ошибка / исключение Основная причина
ZeroDivisionError Деление или остаток от деления на ноль
NameError Имя не найдено
UnboundLocalError Локальное имя используется до присваивания значения
SyntaxError Нарушен синтаксис Python
IndentationError Нарушена структура отступов
TypeError Операция получила объект неподходящего типа
ValueError Тип подходит, но конкретное значение недопустимо

12. Иерархия некоторых изученных исключений

Классы исключений связаны наследованием.

Например:

NameError
└── UnboundLocalError

и:

SyntaxError
└── IndentationError

Поэтому UnboundLocalError является более конкретным видом NameError, а IndentationError — более конкретным видом SyntaxError.

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


13. Как работать с незнакомой ошибкой

Не нужно пытаться запомнить все существующие исключения Python.

Если появилась незнакомая ошибка:

  1. прочитайте последнюю строку traceback;
  2. найдите название исключения;
  3. прочитайте описание;
  4. посмотрите строку кода, на которой оно возникло;
  5. определите причинно-следственную связь: какую операцию выполняла программа и почему эти данные для неё не подошли.

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

Официальная документация

Built-in Exceptions — полный список встроенных исключений Python

Дополнительно:

Errors and Exceptions — учебный раздел документации Python

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