Ошибки в Python: как читать traceback и исправлять исключения
Как читать traceback в Python снизу вверх, чем SyntaxError отличается от исключений, таблица встроенных ошибок и отладка через print, logging и pdb.
Содержание · 9 разделов
- Что такое исключение в Python
- Как читать traceback снизу вверх
- Traceback, который проходит через библиотеки
- Несколько traceback подряд: цепочки исключений
- SyntaxError и исключения во время выполнения
- Встроенные исключения Python: таблица
- Иерархия исключений
- Обработка исключений: try/except
- Как отлаживать ошибки
- print()
- logging
- pdb и breakpoint()
- Отладчик в IDE
- Алгоритм: что делать, когда Python выдал ошибку
- Частые вопросы
- Почему в traceback путь к файлам библиотеки, а не к моему коду?
- Как вывести traceback, не останавливая программу?
- Что значит «Python was not found» в Windows?
- Ошибка No module named после установки пакета — что не так?
- Как сохранить traceback в файл?
- Что значит «Previous line repeated 996 more times»?
- Итог
Когда программа на Python падает, интерпретатор печатает traceback — отчёт о том, какая ошибка произошла и по какой цепочке вызовов программа к ней пришла. Ошибки во время выполнения в Python называются исключениями (exceptions): у каждого есть тип (TypeError, KeyError…) и сообщение.
Быстрый ответ — как читать любую ошибку в Python:
- Последняя строка — тип исключения и сообщение. С неё и начинайте.
- Строки над ней — файл, номер строки и код, где исключение возникло.
- Выше — кто вызвал эту функцию, и так до точки входа в программу.
В этом руководстве разберём устройство traceback на реальных примерах, отличие синтаксических ошибок от исключений, таблицу самых частых встроенных исключений и приёмы отладки — от print() до отладчика pdb. Примеры проверены на Python 3.12.
Что такое исключение в Python
Исключение — это объект, который Python создаёт, когда операция не может быть выполнена: деление на ноль, обращение к несуществующему ключу словаря, открытие отсутствующего файла. Исключение «всплывает» вверх по цепочке вызовов. Если его нигде не перехватили конструкцией try/except, программа завершается и печатает traceback.
prices = {"apple": 100}
print(prices["banana"])
Traceback (most recent call last):
File "C:\projects\shop\prices.py", line 2, in <module>
print(prices["banana"])
~~~~~~^^^^^^^^^^
KeyError: 'banana'
В разговорной речи «ошибка» и «исключение» — синонимы. Формально все встроенные ошибки — классы исключений, унаследованные от BaseException, а почти все, которые стоит обрабатывать в коде, — от Exception.
Как читать traceback снизу вверх
Возьмём программу, которая считает средний чек по клиентам:
def average_price(items):
total = sum(item["price"] for item in items)
return total / len(items)
def report(orders):
for name, items in orders.items():
print(name, average_price(items))
orders = {
"Анна": [{"price": 100}, {"price": 300}],
"Борис": [],
}
report(orders)
Запуск даёт такой вывод:
Анна 200.0
Traceback (most recent call last):
File "C:\projects\shop\orders.py", line 15, in <module>
report(orders)
File "C:\projects\shop\orders.py", line 8, in report
print(name, average_price(items))
^^^^^^^^^^^^^^^^^^^^
File "C:\projects\shop\orders.py", line 3, in average_price
return total / len(items)
~~~~~~^~~~~~~~~~~~
ZeroDivisionError: division by zero
Читаем снизу вверх:
ZeroDivisionError: division by zero— что случилось: деление на ноль.File "...orders.py", line 3, in average_price— где: строка 3, функцияaverage_price, выражениеtotal / len(items). Значки~и^(появились в Python 3.11) подчёркивают точное место: оператор/.line 8, in report— функцию вызвали изreport()в строке 8.line 15, in <module>— аreport()вызвали с верхнего уровня скрипта.
Заголовок Traceback (most recent call last) как раз и означает «самый последний вызов — внизу». Каждая пара строк File … + код называется кадром (frame). Вывод: у Бориса пустой список заказов, len(items) равно нулю. Исправление — проверить пустой список до деления.
Анна 200.0 успела напечататься — всё, что выполнилось до исключения, уже выполнено.
Traceback, который проходит через библиотеки
Часто нижние кадры указывают не на ваш код, а на стандартную библиотеку или установленный пакет:
Traceback (most recent call last):
File "C:\projects\shop\config.py", line 6, in load_config
return json.loads(text)
^^^^^^^^^^^^^^^^
File "C:\Python312\Lib\json\__init__.py", line 346, in loads
return _default_decoder.decode(s)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Python312\Lib\json\decoder.py", line 338, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Python312\Lib\json\decoder.py", line 354, in raw_decode
obj, end = self.scan_once(s, idx)
^^^^^^^^^^^^^^^^^^^^^^
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
Ошибка почти никогда не в коде json или Django — библиотека просто получила неподходящие данные. Найдите последний снизу кадр из вашего проекта (здесь — config.py, строка 6) и проверьте, что вы туда передали. В этом примере в json.loads() попала строка с некорректным JSON.
Несколько traceback подряд: цепочки исключений
Если исключение возникло во время обработки другого, Python покажет оба. Две фразы-разделителя означают разное:
Traceback (most recent call last):
File "C:\projects\shop\handler.py", line 3, in <module>
value = data["b"]
~~~~^^^^^
KeyError: 'b'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\projects\shop\handler.py", line 5, in <module>
print(undefined_name)
^^^^^^^^^^^^^^
NameError: name 'undefined_name' is not defined
- During handling of the above exception, another exception occurred — новая ошибка случилась внутри блока
except, скорее всего, случайно. Чинить нужно обе: и исходную, и ошибку в обработчике. - The above exception was the direct cause of the following exception — программист сознательно заменил одно исключение другим через
raise ... from e. Верхний traceback — первопричина, нижний — то, как её описал код.
Как правильно перевыбрасывать исключения, разобрано в статье try/except в Python.
SyntaxError и исключения во время выполнения
Ошибки в Python бывают двух видов.
Синтаксические ошибки обнаруживаются до запуска: Python не может разобрать код и не выполняет ни одной строки. Traceback у них короткий, без заголовка Traceback (most recent call last):
File "C:\projects\shop\app.py", line 1
print("hello"
^
SyntaxError: '(' was never closed
Исключения во время выполнения возникают, когда синтаксически верный код натыкается на невозможную операцию. До этого момента программа работает, поэтому часть вывода может успеть напечататься.
Частые синтаксические ошибки и их сообщения в Python 3.12:
| Сообщение | Причина |
|---|---|
SyntaxError: '(' was never closed | Не закрыта скобка |
SyntaxError: unterminated string literal (detected at line 1) | Не закрыта кавычка |
SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='? | = вместо == в условии if |
IndentationError: expected an indented block after 'if' statement on line 1 | Нет отступа после if, for, def |
IndentationError: unexpected indent | Лишний отступ |
TabError: inconsistent use of tabs and spaces in indentation | Смешаны табы и пробелы |
Важная деталь: номер строки в SyntaxError — место, где парсер заметил проблему. Незакрытая скобка в строке 10 может «всплыть» ошибкой на строке 11 или ниже, поэтому смотрите и на предыдущие строки. Правила отступов и синтаксиса собраны в шпаргалке по синтаксису Python.
Встроенные исключения Python: таблица
Сообщения ниже — вывод Python 3.12 (у ImportError Python дополнительно печатает путь к модулю и подсказку).
| Исключение | Пример сообщения | Типичная причина |
|---|---|---|
NameError | name 'nme' is not defined. Did you mean: 'name'? | Опечатка в имени, переменная не создана |
TypeError | can only concatenate str (not "int") to str | Операция с несовместимыми типами |
TypeError | object of type 'int' has no len() | Функция получила объект не того типа |
ValueError | invalid literal for int() with base 10: 'abc' | Тип верный, значение — нет |
AttributeError | 'NoneType' object has no attribute 'upper' | Метода нет у объекта; часто переменная оказалась None |
KeyError | 'b' | Нет такого ключа в словаре |
IndexError | list index out of range | Индекс за пределами списка |
ZeroDivisionError | division by zero | Деление на ноль |
FileNotFoundError | [Errno 2] No such file or directory: 'missing.txt' | Неверный путь или рабочий каталог |
ModuleNotFoundError | No module named 'requestz' | Пакет не установлен или опечатка в имени |
ImportError | cannot import name 'pathx' from 'os' | Модуль есть, но имени в нём нет |
UnicodeDecodeError | 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte | Файл в другой кодировке |
RecursionError | maximum recursion depth exceeded | Бесконечная рекурсия |
Python 3.12 часто подсказывает исправление прямо в сообщении — Did you mean: 'name'?. Читайте сообщение целиком: подсказка обычно верная.
Пара пояснений к частым случаям:
KeyErrorиIndexError— при чтении словаря используйтеdict.get(key, default), если ключа может не быть (подробнее — в статье про словари Python); для списков проверяйте длину или итерируйтесь без индексов (см. списки в Python).TypeError— операция получила объект не того типа: строку вместо числа,Noneвместо списка, лишний аргумент. Типичные сообщения и исправления собраны в статье «TypeError в Python».ValueError— тип подходит, а значение нет:int("abc"), распаковка списка не той длины. Разбор частых сообщений — в статье «ValueError в Python».AttributeError: 'NoneType' object has no attribute— функция вернулаNoneвместо ожидаемого объекта. Классика:items = items.sort()— методsort()сортирует на месте и возвращаетNone. Другие причины, включаяmodule has no attribute, — в статье об AttributeError в Python.ImportError: cannot import name— модуль найден, но нужного имени в нём нет: опечатка, другая версия библиотеки или циклический импорт. Как найти причину — в статье «ImportError в Python».ModuleNotFoundError— самая частая ошибка начинающих, у неё много причин (не тот интерпретатор, venv, имя пакета ≠ имя модуля). Подробный разбор — в статье ModuleNotFoundError: No module named.
Иерархия исключений
Исключения — классы, связанные наследованием. Это важно для обработки: except LookupError поймает и KeyError, и IndexError.
BaseException
├── KeyboardInterrupt # Ctrl+C
├── SystemExit # sys.exit()
└── Exception
├── ArithmeticError
│ └── ZeroDivisionError
├── ImportError
│ └── ModuleNotFoundError
├── LookupError
│ ├── IndexError
│ └── KeyError
├── OSError
│ ├── FileNotFoundError
│ └── PermissionError
├── RuntimeError
│ └── RecursionError
├── SyntaxError
│ └── IndentationError
│ └── TabError
├── TypeError
└── ValueError
└── UnicodeError
└── UnicodeDecodeError
KeyboardInterrupt и SystemExit наследуются не от Exception, поэтому except Exception не мешает остановить программу через Ctrl+C. Полное дерево — в документации Python.
Обработка исключений: try/except
Если ошибка ожидаема — пользователь ввёл не число, файл может отсутствовать, сеть недоступна, — её перехватывают:
raw = input("Возраст: ")
try:
age = int(raw)
except ValueError:
print("Введите число")
Перехватывайте конкретные исключения и только там, где знаете, что делать дальше. Голый except: и «проглатывание» ошибок прячут баги. Блоки else и finally, raise from, собственные классы исключений и ExceptionGroup разобраны в отдельной статье — try/except в Python: обработка исключений.
Как отлаживать ошибки
Traceback говорит, где упала программа. Чтобы понять, почему, нужно посмотреть значения переменных в этот момент.
print()
Самый быстрый способ — вывести значения перед проблемной строкой:
def average_price(items):
total = sum(item["price"] for item in items)
print(f"{items=} {total=}") # отладочный вывод
return total / len(items)
items=[{'price': 100}, {'price': 300}] total=400
items=[] total=0
Спецификатор = в f-строке (f"{items=}") печатает и имя, и значение. Не забудьте удалить отладочные print() после исправления.
logging
Для кода, который работает долго или на сервере, вместо print() используют модуль logging. Метод logger.exception() внутри except записывает сообщение вместе с полным traceback:
import logging
logging.basicConfig(level=logging.INFO)
log = logging.getLogger("app")
try:
1 / 0
except ZeroDivisionError:
log.exception("Не удалось посчитать")
ERROR:app:Не удалось посчитать
Traceback (most recent call last):
File "C:\projects\shop\calc.py", line 7, in <module>
1 / 0
~~^~~
ZeroDivisionError: division by zero
Если нужно получить traceback строкой (например, чтобы отправить в мессенджер), используйте traceback.format_exc(), а для печати — traceback.print_exc() из стандартного модуля traceback.
pdb и breakpoint()
Встроенный отладчик pdb останавливает программу и даёт выполнять команды в её контексте. Самый простой способ — вставить вызов breakpoint() (доступен с Python 3.7) в нужное место:
def average_price(items):
total = sum(item["price"] for item in items)
breakpoint()
return total / len(items)
При запуске программа остановится, и появится приглашение (Pdb):
> c:\projects\shop\bp.py(4)average_price()
-> return total / len(items)
(Pdb) p total
400
(Pdb) p len(items)
2
(Pdb) c
Основные команды pdb:
| Команда | Действие |
|---|---|
p выражение | Напечатать значение (pp — красиво) |
n | Выполнить текущую строку, не заходя в функции |
s | Шаг с заходом внутрь вызываемой функции |
c | Продолжить выполнение до следующей точки остановки |
l | Показать код вокруг текущей строки |
w | Показать стек вызовов (как в traceback) |
q | Выйти из отладчика |
Можно не вставлять breakpoint() заранее, а запустить скрипт под отладчиком так, чтобы он остановился в момент падения (post-mortem):
python -m pdb -c continue orders.py
После исключения отладчик выведет Uncaught exception. Entering post mortem debugging и откроет (Pdb) прямо в кадре, где произошла ошибка, — можно напечатать p items и увидеть пустой список. Переменная окружения PYTHONBREAKPOINT=0 отключает все вызовы breakpoint(), но удалять их из кода перед коммитом всё равно нужно.
Отладчик в IDE
В VS Code и PyCharm то же самое делается мышью: клик слева от номера строки ставит точку остановки, а запуск в режиме Debug (F5 в VS Code) показывает значения переменных в боковой панели. Если программа запускается не тем интерпретатором, отладчик тоже «увидит» не те пакеты — выберите интерпретатор виртуального окружения.
Алгоритм: что делать, когда Python выдал ошибку
- Прочитайте последнюю строку: тип исключения и сообщение, включая подсказку
Did you mean. - Найдите последний кадр из своего кода и откройте эту строку.
- Если это
SyntaxError— проверьте и строку выше: незакрытые скобки и кавычки. - Посмотрите значения переменных в этой точке:
print(),breakpoint()или отладчик IDE. - Если причина неочевидна — скопируйте последнюю строку traceback (без путей к своим файлам) в поиск: типовые сообщения разобраны тысячи раз.
- Исправьте причину, а не симптом:
try/exceptвокруг ошибки — не исправление, если вы не знаете, почему она возникла.
Частые вопросы
Почему в traceback путь к файлам библиотеки, а не к моему коду?
Ошибка возникла внутри библиотеки, но из-за данных, которые передал ваш код. Поднимайтесь по traceback вверх, пока не найдёте файл своего проекта, — там и ищите причину.
Как вывести traceback, не останавливая программу?
Перехватите исключение и вызовите traceback.print_exc() или logging.exception() внутри блока except. Программа продолжит работу, а полный traceback попадёт в консоль или лог.
Что значит «Python was not found» в Windows?
Это сообщение выдаёт сама Windows ещё до запуска Python: команда python ведёт на ярлык Microsoft Store, а интерпретатор не установлен или не добавлен в PATH. Как это исправить — в руководстве как установить Python.
Ошибка No module named после установки пакета — что не так?
Скорее всего, пакет установлен в другой интерпретатор. Сравните sys.executable скрипта с выводом python -m pip --version и прочитайте статью как установить библиотеку в Python.
Как сохранить traceback в файл?
В долгоживущих программах настройте logging с записью в файл — тогда log.exception() сохранит traceback вместе со временем и уровнем сообщения. Для разового случая хватит traceback.print_exc() с параметром file:
import traceback
try:
1 / 0
except ZeroDivisionError:
with open("errors.log", "a", encoding="utf-8") as log_file:
traceback.print_exc(file=log_file)
Что значит «Previous line repeated 996 more times»?
Python сократил traceback: одни и те же кадры повторились сотни раз подряд. Почти всегда это RecursionError — функция вызывает саму себя без условия выхода. Смотрите на кадры перед этой строкой: они показывают, какая функция зациклилась.
Итог
Traceback читается снизу вверх: последняя строка говорит, что случилось, кадры над ней — где и по какой цепочке вызовов. Синтаксические ошибки не дают программе запуститься, исключения возникают во время работы и перехватываются через try/except. Для поиска причины хватает print(), logging и breakpoint(). Разборы конкретных ошибок — в разделе «Ошибки и исключения»: начните с ModuleNotFoundError и обработки исключений через try/except.
- #Python
- #Traceback
- #Исключения
- #Отладка
- #pdb