Ошибки — это способ Python рассказать, что случилось
Каждая ошибка Python — это объект с типом, сообщением и трейсбэком — цепочкой вызовов, приведших к ней. Умение хорошо её читать — главный отладочный навык, который ты можешь развить. Эта страница — обзор ошибок, которые ты реально встретишь, и привычек, которые быстро их чинят.
Глубже про механику try/except и собственный raise — страница про исключения. Здесь — о конкретных ошибках и чтении их сообщений.
Читаем трейсбэк
Запусти что-то ломающееся:
def divide(a, b):
return a / b
def report(values):
for v in values:
print(divide(10, v))
report([5, 2, 0])
Python распечатает нечто вроде:
Traceback (most recent call last):
File "script.py", line 8, in <module>
report([5, 2, 0])
File "script.py", line 6, in report
print(divide(10, v))
File "script.py", line 2, in divide
return a / b
ZeroDivisionError: division by zero
Читаем снизу вверх:
ZeroDivisionError: division by zero— тип исключения и сообщение. Это что пошло не так.return a / bвdivide, строка 2 — строка, которая реально подняла ошибку.print(divide(10, v))вreport, строка 6 — вызов, вызвавший ошибку.report([5, 2, 0])на уровне модуля, строка 8 — откуда началось.
Нижний фрейм почти всегда — место ремонта. Когда библиотека кидает ошибку глубоко в своих недрах, поднимайся по трейсбэку до первого фрейма в твоём коде — это и есть вызов, в который ты передал плохой вход.
Ошибки, которые встретишь чаще всего
NameError
«Name 'mesage' is not defined». Опечатка или использование переменной до присваивания. На свежих версиях Python обычно подсказывает подходящее имя («Did you mean 'message'?»).
Ремонт: проверь написание и область видимости. Если имя существует только внутри функции, снаружи его не прочесть.
TypeError
«Can only concatenate str (not 'int') to str». Не тот тип для операции. Классика: сложить строку с числом, вызвать что-то невызываемое, передать в функцию не то число аргументов.
Ремонт: явное преобразование типов (str(30), int("30")) или разбор того, что ты реально передал. f-строка обычно читается лучше, чем +-конкатенация разных типов: f"age: {30}".
ValueError
«Invalid literal for int() with base 10: 'hello'». Тип правильный — int() принимает строку, — но значение не годится. Частое у int(), float(), разбора datetime и функций с ограниченным диапазоном аргумента.
Ремонт: валидируй до преобразования или лови ошибку:
KeyError
«KeyError: 'charlie'». Ключа в словаре нет. Три идиоматических ремонта, под разные намерения:
Для словаря, в котором отсутствующие ключи должны авто-инициализироваться, стоит взглянуть на collections.defaultdict.
IndexError
«List index out of range». Ты попросил позицию, которой нет в последовательности.
Ремонт: проверка длины, -1 для последнего элемента или срез (numbers[5:6] вернёт [] вместо ошибки).
AttributeError
«'NoneType' object has no attribute 'upper'». Ты вызвал метод у объекта, у которого его нет. Почти всегда значит, что переменная — не того типа, часто None там, где ждал реальное значение.
Ремонт: выясни, откуда пришёл None. print(type(var)) или брейкпойнт прямо перед ошибкой — самый быстрый путь. Функции, которые «иногда падают», обычно возвращают None; проверяй возврат до вызова методов на нём.
ModuleNotFoundError (и ImportError)
import fastapi
«No module named 'fastapi'». Пакет не установлен в тот Python, которым запускается скрипт. Две частых причины:
- Ты реально его не поставил. Запусти
python -m pip install fastapiв правильном окружении. - Поставил, но в другой Python. На macOS это случается постоянно, когда
pipиpythonсмотрят на разные установки.
Надёжный ремонт — ставить тем же интерпретатором, которым запускаешь:
python -m pip install fastapi
Если используешь виртуальное окружение (а стоит), убедись, что оно активировано и перед pip install, и перед python script.py.
FileNotFoundError
with open("settings.yaml") as f:
config = f.read()
«[Errno 2] No such file or directory: 'settings.yaml'». Путь не существует относительно того места, где запускается скрипт.
Ремонт: распечатай os.getcwd() в начале скрипта, чтобы увидеть, где Python ищет. Используй абсолютные пути или pathlib.Path(__file__).parent / "settings.yaml", чтобы привязать путь к расположению скрипта.
EOFError
name = input("Name: ")
«EOF when reading a line». input() пытался прочесть из stdin и не получил ничего — либо пайп пустой, либо ты нажал Ctrl-D в приглашении.
Ремонт: если пайп легитимен — оберни вызов:
try:
name = input("Name: ")
except EOFError:
name = "anonymous"
В браузерном редакторе в этих документах рантайм симулирует ввод; здесь ты на это не наткнёшься.
IndentationError и SyntaxError
IndentationError: expected an indented block after function definition on line 2
Эти особенные — они срабатывают до запуска программы. Python отказался парсить файл.
IndentationError— телоdef,if,forи т. д. отсутствует или сбито. Большинство редакторов визуализируют отступы; включи «show whitespace», чтобы замечать смешанные табы и пробелы.SyntaxError— забыл двоеточие, не совпали скобки или опечатался в ключевом слове. Свежие версии Python указывают стрелкой (^) на виноватый символ.
Ремонт: сообщение называет строку. Иди и смотри. Если на ней ничего явного — проверь строку выше: незакрытая скобка несколько строк назад часто всплывает как синтаксическая ошибка значительно позже.
RuntimeError
Универсальный «что-то пошло не так во время выполнения, и это не одно из более специфичных». RecursionError (превышение глубины рекурсии) — частая специализация. Библиотечный код часто кидает RuntimeError, когда находится в плохом состоянии.
Ремонт: читай сообщение, оно обычно описательное. Если дело в рекурсии — либо перепиши итеративно, либо в редких случаях подкрути sys.setrecursionlimit.
Отладка через print
Прежде чем тянуться к настоящему отладчику, print() — а лучше форма f"{var=}" — ловит большинство багов:
f"{var=}" печатает и имя выражения, и его значение — не надо переписывать имя в формате. Запускаешь скрипт, сканируешь вывод, находишь строку, где значение стало не тем. Большинство «таинственных» багов становятся очевидными после трёх-четырёх удачно поставленных print-ов.
Прежде чем коммитить, убери print-ы. logging.debug(...) приятнее, чем print, для кода, идущего в продакшн — отладочное логирование можно включать и выключать без правки строк.
breakpoint() и pdb
Когда print не хватает, breakpoint() опускает в интерактивный отладчик Python в этой точке:
def discount(price, percent):
breakpoint()
return price * (1 - percent / 100)
discount(100, 20)
Запусти скрипт в настоящем терминале. Окажешься в приглашении (Pdb). Команды, которые стоит знать:
p variable— распечатать переменную.n— шагнуть на следующую строку.s— войти в вызов функции.c— продолжить до следующего брейкпойнта или конца.q— выйти.l— вывести исходник вокруг текущей строки.
Отладчики IDE (VS Code, PyCharm) оборачивают тот же протокол в GUI — брейкпойнты в полях, сайдбар с переменными. Выбирай то, что меньше трения.
Привычки, уменьшающие количество ошибок
- Используй описательные имена переменных. Половина шума
TypeErrorиAttributeErrorисчезает, когда ты не можешь забыть, что лежит в переменной. - Валидируй входы на границе. Распарсь и проверь пользовательский ввод или содержимое файла один раз в начале функции. Остальной код может доверять значениям.
- Падай громко, не тихо. Голый
except Exception: passпрячет ошибки, которые тебе на самом деле нужно видеть. Лови конкретные исключения, осознанно их обрабатывай, а остальное — пусть всплывает. - Сначала читай низ трейсбэка. Каждая минута, потраченная на обучение чтению трейсбэков, окупается стократ.
Большинство ошибок — не загадки, а однострочные опечатки или неверные типы с ясным сообщением. Доверяй сообщению об ошибке: оно обычно право.
У тебя получилось
Это конец справочной части. Ты прошёл от «что такое Python?» через переменные, управление потоком, коллекции, функции, классы, итерацию, реальные данные и ошибки. Дальше шаг — проектный: выбери то, что хочется построить, и двигайся к тем частям, которые нужно выучить глубже. Эти страницы никуда не денутся, когда вернёшься с конкретным вопросом.
Часто задаваемые вопросы
Что такое KeyError в Python?
KeyError поднимается при попытке получить в словаре ключ, которого нет. users["missing"] даст KeyError: 'missing'. Безопасные альтернативы: users.get("missing", default), users.get("missing") (вернёт None) или явная проверка if key in users:.
Что такое EOFError в Python?
EOFError (end-of-file) поднимается, когда input() не может ничего прочитать из-за закрытого потока ввода. Чаще всего видишь, когда скрипту с input() ничего не подают через пайп, или когда нажал Ctrl-D в интерактивной сессии. Оберни в try/except EOFError, если скрипт должен уметь работать с пайпом.
Что такое ModuleNotFoundError?
ModuleNotFoundError означает, что import X не нашёл модуль X. Либо пакет не установлен (pip install X), либо установлен в другой Python, а не в тот, что запускает код (частая история, когда стоит несколько Python-ов). python -m pip install X чинит второй случай, используя тот же интерпретатор.
Как читать трейсбэк Python?
Читай снизу вверх. Последняя строка — тип исключения и сообщение: конкретно то, что пошло не так. Строки выше — стек вызовов, приведших сюда, твой код обычно ближе к низу. Перейди к файлу+строке в самом нижнем твоём фрейме — туда и идёт ремонт.