SkillPy

Ошибки и исключения в Python: try, except, else, finally

Время чтения20 мин чтения
Опубликовано22 октября 2026 г.
Средне
Python
Ошибки
Исключения
Начинающим
Ошибки и исключения в Python: try, except, else, finally
Кот-наставник

— Привет! Сейчас разберём тему, из-за которой новички чаще всего бросают обучение: ошибки.

— Спойлер: traceback не ругается, а рассказывает, где искать. Ошибка — это данные, а не приговор. Разберёмся, как её читать и обрабатывать.

Ошибки и исключения в Python — это не сбой системы, а встроенный механизм обратной связи. Когда программа делает то, что Python выполнить не может — делит на ноль или ищет несуществующее имя, — интерпретатор останавливается и показывает traceback: тип ошибки, сообщение и цепочку строк, по которой до неё дошло. В этом гайде научимся читать traceback, перехватывать ошибки через try, except, else и finally, поднимать собственные через raise и отличать честную обработку от маскировки дефекта. Все примеры запускаются в Python 3.12.

Ошибки в Python: коротко

Ошибка останавливает программу и показывает traceback. Последняя строка traceback — тип и причина, строки выше — путь выполнения до неё. Перехватывают ошибку конструкцией try/except, причём перехватывать нужно конкретный тип. Минимальный пример:

try:
    print(10 / 0)
except ZeroDivisionError:
    print("На ноль делить нельзя")
На ноль делить нельзя

Что такое исключение в Python?

Исключение (exception) — сигнал о том, что выполнение пошло по пути, который Python завершить не может: деление на ноль, обращение к несуществующему ключу, чтение несуществующего файла.

Когда возникает исключение, Python «поднимает» его (в терминах языка — raise), и оно распространяется вверх по цепочке вызовов, пока его кто-то не перехватит (except) или пока программа не остановится, показав traceback — отчёт о том, что произошло.

Термины «ошибка» и «исключение» в Python почти синонимы: все встроенные ошибки — NameError, ValueError, KeyError и остальные — это классы исключений. Разница скорее в оттенке: «ошибка» — это ситуация, «исключение» — механизм, которым Python о ней сообщает. Ключевая мысль для новичка: падение программы — это не «Python сломался». Это Python говорит: «я дошёл до места, где мои правила не позволяют продолжить, — вот тип проблемы и вот где это случилось».

Как читать traceback?

Traceback — главный инструмент отладки, и читается он не так, как обычный текст. Разберём на программе, которая считает среднюю цену:

def average(total, count):
    """Делит total на count."""
    return total / count

def report(prices):
    return average(sum(prices), len(prices))

print(report([]))

Ошибка выполнения — main.py, строка 3

ZeroDivisionError: division by zero

Читай traceback снизу вверх. Последняя строка — самая важная: ZeroDivisionError: division by zero означает «ошибка деления на ноль». Строки выше — цепочка вызовов: выполнение началось со строки 8, зашло в report на строке 6, оттуда в average на строке 3, где и случилось деление на ноль. Номера строк указывают, куда смотреть в коде. В примере список prices пуст, поэтому sum(prices) равен нулю, а len(prices) — тоже нулю: деление 0 / 0 и даёт ошибку. Traceback не предлагает решение — он точно сообщает факты, а причину находишь уже ты.

Разбери traceback по строкам

Кликни по строке отчёта — блок покажет строку кода, на которую она указывает, и пояснение.

Код программы main.py
def average(total, count):
    """Делит total на count."""
    return total / count

def report(prices):
    return average(sum(prices), len(prices))

print(report([]))
Traceback — читай снизу вверх
Traceback (most recent call last):
  File "main.py", line 8, in <module>
    print(report([]))
  File "main.py", line 6, in report
    return average(sum(prices), len(prices))
  File "main.py", line 3, in average
    return total / count
ZeroDivisionError: division by zero
  1. Старт программы

    File "main.py", line 8, in <module>

    print(report([]))

    С этой строки всё началось: программа вызвала report([]) с пустым списком. Верхние строки traceback — это путь выполнения: ошибки тут ещё нет.

  2. Проход через report()

    File "main.py", line 6, in report

    return average(sum(prices), len(prices))

    Выполнение зашло в report: список prices пуст, поэтому sum(prices) = 0 и len(prices) = 0 — вызывается average(0, 0). Строка ещё не завершилась: ждёт результата.

  3. Место падения

    File "main.py", line 3, in average

    return total / count

    Здесь всё и остановилось: total / count превратилось в 0 / 0. Деление на ноль — операция, которую Python выполнить не может.

  4. Тип и причина

    ZeroDivisionError: division by zero

    Последняя строка — самое главное: тип ошибки ZeroDivisionError и причина division by zero. Начинай диагностику отсюда и читай traceback снизу вверх.

Последняя строка traceback — тип ошибки и причина, строки выше — цепочка вызовов до неё. Читай снизу вверх: сначала тип и причина, затем место падения, затем путь выполнения.

Частые ошибки новичка и что они означают

Каждый тип ошибки — это отдельная ситуация со своей причиной. Вот самые частые на старте:

ОшибкаЧто означаетГде разобрано
NameErrorИмя переменной или функции не найденоNameError: как исправить
KeyErrorВ словаре нет такого ключаKeyError в Python: как исправить
ValueErrorТип подходящий, но значение не тоValueError: invalid literal for int() — как исправить
TypeErrorНе подходит сам тип значенияTypeError: can only concatenate str
IndexErrorНомер за пределами списка или строкиIndexError: list index out of range
IndentationErrorНеверные отступыIndentationError: unexpected indent
UnboundLocalErrorЛокальная переменная прочитана до присваиванияUnboundLocalError: причина и как исправить
ZeroDivisionErrorДеление на нольРазобрана в этом гайде ниже

Обрати внимание: ошибки различаются не по «серьёзности», а по ситуации. Одна и та же строка print(data[5]) даст IndexError для списка из трёх элементов, KeyError — если data словарь без ключа 5, и TypeError — если data вообще не поддерживает доступ по индексу. Точный тип ошибки — половина диагностики.

try и except: перехватываем ошибку

Падение не всегда уместно. Если ошибка — предусмотренная часть работы программы (пользователь ввёл не число, файла может не быть), её перехватывают и обрабатывают. Конструкция такая:

def average(total, count):
    return total / count

prices = []

try:
    print(average(sum(prices), len(prices)))
except ZeroDivisionError:
    print("Список пуст — среднее не посчитать")
Список пуст — среднее не посчитать

В блоке try находится код, который может упасть. Если внутри возникает ZeroDivisionError, выполнение переходит в блок except с тем же типом, и программа продолжает жить. Если бы ошибка была другого типа, например NameError, этот except её бы не поймал — и программа упала бы как обычно. Важно: в try клади только то, что действительно может упасть. Если обернуть в try весь файл, станет непонятно, какая именно строка и с какой ошибкой защищена.

Перехватывай конкретный тип, а не всё подряд

Для разных ситуаций можно написать несколько блоков except — по одному на тип ошибки:

try:
    value = int(input("Введи число: "))
    print(100 / value)
except ValueError:
    print("Это не число")
except ZeroDivisionError:
    print("На ноль делить нельзя")
Это не число

Ввод пользователя: 0

На ноль делить нельзя

Синтаксическая ошибка — main.py, строка 1

SyntaxError: invalid syntax (строка 1)

Неверный ввод даёт ValueError, ноль — ZeroDivisionError, и каждый обрабатывается своим способом. Если бы подошли оба типа, их можно перечислить в одном блоке через запятую: except (ValueError, ZeroDivisionError):.

else: код, который выполняется только при успехе

Блок else относится не к if, а к try: он выполняется, только если ошибки не было. В него кладут действия, которые нужны лишь при успешном выполнении защищённого кода:

try:
    number = int(input("Введи число: "))
except ValueError:
    print("Это не число")
else:
    print("Удвоенное число:", number * 2)
Удвоенное число: 50

Ввод пользователя: двадцать

Это не число

Синтаксическая ошибка — main.py, строка 1

SyntaxError: invalid syntax (строка 1)

Зачем else, если можно просто продолжить код после try/except? Затем, что else отделяет успешную ветку от защищаемой операции. Код после всей конструкции выполнится и при ошибке, а код в else — нет. Так читатель сразу видит: вот опасная операция, вот её обработка, а вот то, что делаем только при успехе.

finally: код, который выполняется всегда

Блок finally выполняется в любом случае: была ошибка или нет. Его используют для обязательной уборки — закрыть файл, завершить соединение, освободить ресурс:

try:
    result = 100 / int(input("Введи число: "))
except (ValueError, ZeroDivisionError):
    print("Что-то пошло не так")
else:
    print("Результат:", result)
finally:
    print("Попытка завершена")
Что-то пошло не так
Попытка завершена

Сначала отрабатывает except или else, затем — всегда — finally. Даже если бы ошибка вообще не была перехвачена и программа упала, finally всё равно выполнился бы: Python запускает его перед тем, как исключение уйдёт дальше вверх.

raise: поднимаем ошибку сами

Обработка ошибок — это не только их перехват. Иногда правила задаёт твоя функция, и неверные данные — это ошибка, которую должна увидеть вызывающая сторона. Тогда исключение поднимают явно через raise:

def apply_discount(price, percent):
    if not 0 <= percent <= 100:
        raise ValueError("Процент скидки должен быть от 0 до 100")
    return price * (100 - percent) / 100

print(apply_discount(1000, 150))

Ошибка выполнения — main.py, строка 3

ValueError: Процент скидки должен быть от 0 до 100

Функция сама проверяет контракт: процент вне диапазона — это ошибка вызывающего кода, и прятать её нельзя. Сообщение после raise — это текст для человека, который будет читать traceback, поэтому пиши его конкретно: что не так и какой диапазон допустим. Такой подход честнее, чем тихо «чинить» данные внутри функции: если apply_discount сам подрезал бы процент до 100, вызывающий код никогда не узнал бы о своей ошибке.

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

Все встроенные ошибки Python — классы, выстроенные в иерархию. NameError, ValueError, KeyError и остальные наследуются от общего родителя Exception. Отсюда два практических правила:

  • except Exception перехватывает почти все встроенные ошибки — слишком широко для обычной программы.
  • except без типа перехватывает ещё и служебные сигналы вроде KeyboardInterrupt (Ctrl+C) — почти никогда не то, что нужно.

Поэтому в повседневном коде перехватывают конкретный тип: except ValueError, except KeyError, except (ValueError, ZeroDivisionError).

Частые ошибки при обработке ошибок

  • Глотание ошибки: except ValueError: pass — код продолжает работу с неверными данными, причина исчезла навсегда.
  • Перехват до диагноза: сначала понять, почему падает, потом оборачивать в try — иначе защищаешь не ту операцию.
  • Слишком широкий `try`: один блок на сто строк ловит ошибки неясного происхождения.
  • Маскировка дефекта: перехватывать NameError от опечатки вместо исправления имени.
  • Обработчик без сообщения: except ValueError: print("Ошибка") не говорит ни что случилось, ни что делать.

Общий принцип: обработчик отвечает на предусмотренную ситуацию — неверный ввод, отсутствующий файл, сбой сети. Дефект собственного кода — опечатку, неверный порядок строк, забытое присваивание — нужно исправлять, а не перехватывать. Это и есть разница между «ментором, который объясняет причину» и «линтером, который прячет сообщение».

Короткие ответы про ошибки в Python

Что такое исключение в Python?

Сигнал о ситуации, которую Python не может выполнить: деление на ноль, несуществующий ключ или файл. Исключение можно перехватить через try/except или дать программе упасть с traceback.

Как читать traceback?

Снизу вверх. Последняя строка — тип ошибки и сообщение, строки выше — цепочка вызовов с номерами строк, по которой выполнение дошло до ошибки.

В чём разница между ошибкой и исключением?

В Python эти слова почти синонимы: все встроенные ошибки — это классы исключений. «Ошибка» описывает ситуацию, «исключение» — механизм, которым Python о ней сообщает.

Когда нужен else в try?

Когда действие нужно выполнить только при успехе защищённого кода. Если поставить его просто после конструкции, оно выполнится и после перехваченной ошибки.

Что делает finally?

Выполняется всегда — и при успехе, и при ошибке, и даже при необработанном падении. Нужен для обязательной уборки: закрыть файл, освободить ресурс.

Почему не стоит перехватывать все ошибки подряд?

except без типа ловит всё, включая опечатки и Ctrl+C. Программа не падает, но причина спрятана: явная ошибка превращается в тихий неверный результат.

Главное про ошибки и исключения в Python:

  1. Ошибка — это данные, а не приговор

    Traceback точно сообщает тип ошибки, сообщение и путь выполнения. Читай его снизу вверх и начинай с последней строки.

  2. Перехватывай конкретный тип

    except ValueError, except ZeroDivisionError, а не всё подряд. Несколько типов — в скобках через запятую.

  3. Конструкция — это четыре блока

    try защищает, except обрабатывает, else выполняется только при успехе, finally — всегда.

  4. Свои правила — через raise

    Функция поднимает исключение, когда входные данные нарушают её контракт, и сообщает причину человеку.

  5. Обработчик — для предусмотренных ситуаций

    Неверный ввод, отсутствующий файл, сбой сети — обрабатывай. Опечатку и неверный порядок строк — исправляй.

Кот-наставник

— Запомни порядок: сначала прочитай последнюю строку traceback, потом реши, эта ошибка — часть жизни программы или дефект кода. Первую обрабатывай через try, второй — чини источник. Тогда ошибки перестанут пугать и начнут экономить время.

Научись читать ошибки, а не бояться их

На SkillPy ты пишешь код сам, а AI-наставник разбирает именно твою ошибку: объясняет traceback, причину и следующий шаг. Ошибка превращается в подсказку. Первые задачи доступны бесплатно.