Лекция
Отладка: логи, трассировка и ошибки новичков
Научитесь отлаживать код с помощью логов и трассировки, избегайте распространенных ошибок новичков.
- отладка
- логирование
- трассировка
- ошибки новичков
- программирование
- дебаггинг
- код
- информатика
Для звонков по России
Личный кабинет
Лекция
Научитесь отлаживать код с помощью логов и трассировки, избегайте распространенных ошибок новичков.
Подскажем по теме, разберём задание и поможем довести работу до результата.
Баг — это несоответствие между ожидаемым блюдом и тем, что реально получилось на выходе. Если рецепт предписывает литра супа, а на выходе — перед вами баг, требующий расследования.
Чтобы его устранить, работают в три приёма:
Диаграмма загружается…
| Кухня | Программирование |
|---|---|
| Суп слишком солёный | Переменная total содержит неверное значение |
| Пирог не поднялся | Результат вычисления равен 0 |
| Ингредиент сгорел | Значение перезаписано до использования |
Стандарты оформления кода мы оставим для лекции 12 — здесь фокус на локализации ошибок. Воспроизведение фиксирует симптомы, изоляция находит «испорченный ингредиент», исправление — замена значения.
Пример:
salt = 50
water = 1000
soup = salt * water
print(soup) # Ожидали 1050, получили 50000?
Вместо догадок по внешнему виду блюда используют логирование — как дегустацию на каждом этапе — и трассировку, которая ведёт архив заказов.
Повар не ждёт финального банкета, чтобы оценить блюдо: он дегустирует соус после обжарки, проверяет плотность теста перед духовкой, фиксирует в блокноте «15:30 — слишком кисло». Логирование — это такие же промежуточные пробники состояния программы.
Уровни важности записей — как степени критичности замечаний су-шефу:
| Уровень | Значение | Кулинарный аналог |
|---|---|---|
DEBUG |
Технические детали | Запись температуры плиты каждую минуту |
INFO |
Штатный процесс | «Бульон поставлен, время варки: 2ч» |
ERROR |
Сбой операции | «Молоко свернулось, требуется замена» |
На практике мы расставляем метки в коде — вывод значений ключевых переменных перед вычислениями:
temperature = 85
cooking_time = 45
print("[DEBUG] Начало: t=" + str(temperature) + ", time=" + str(cooking_time))
temperature = temperature + 15
print("[INFO] После нагрева: t=" + str(temperature))
error_code = 0
print("[ERROR] Код ошибки: " + str(error_code))
В реальных проектах записи направляют в файлы или системы мониторинга, но суть остаётся прежней: фиксация состояния на ключевых шагах рецепта позволяет отследить, в какой момент блюдо «испортилось».
Когда заказ поступает на кухню, су-шеф кладёт листок с задачей поверх стопки. Если повар вызывает помощника для нарезки, в стек добавляется ещё одна запись — теперь помощник работает, а повар ждёт. Это стек вызовов: последний пришёл — первым выполнился (LIFO). Каждый уровень — фрейм с локальными ингредиентами и адресом возврата. Когда функция завершается через return, фрейм удаляется, и управление возвращается к предыдущему уровню.
Диаграмма загружается…
При ошибке Python выводит traceback — снимок стека снизу вверх в формате File -> line -> in function. Верхняя строка — точка аварии, нижние — цепочка вызовов, показывающая, кто инициировал проблему. Читать traceback стоит снизу: это путь от входной точки программы до места падения.
В отладчике есть два режима трассировки: Step Over — выполнить вызов как чёрный ящик, не заглядывая внутрь (довериться помощнику), и Step Into — войти внутрь функции и проверить каждое действие пошагово.
# Пример traceback при делении на ноль
Traceback (most recent call last):
File "kitchen.py", line 3, in <module>
cook()
File "kitchen.py", line 7, in cook
chop()
File "kitchen.py", line 11, in chop
result = 1 / 0
ZeroDivisionError: division by zero
Даже при точном соблюдении пропорций на кухне возникают «призраки» — логические ошибки, которые не сопровождаются дымом аварий, но портят итоговый результат. Они бесшумно искажают данные, возвращают пустоту или смешивают типы.
| Ошибка | Симптом в кулинарном процессе | Метод отладки |
|---|---|---|
Забытая соль (отсутствует return) |
Блюдо подано, но тарелка содержит None |
Логирование результата: print("out:", volume) |
| Сдвиг в органайзере (off-by-one) | Обратились к 5-й ячейке вместо 4-й, пересыпав лишнее | Печать индекса и границ перед выборкой |
| Испорченная кладовая (мутация аргумента) | Изменили ингредиент в общем хранилище вместо локальной копии | Лог состояния до операции и после |
| Несовместимость ингредиентов (неявное приведение) | Сложили строку "2" с числом 3, получив "23" |
Проверка типов операндов в логах |
Рассмотрим поимку пустого значения на практике:
soup_volume = cook_soup()
print("DEBUG: получено", soup_volume)
Если в консоли появляется DEBUG: получено None, вы поймали призрака — функция завершилась без возврата данных. Далее разберём системную стратегию борьбы с такими сбоями.
Отладка — не гадание на кофейной гуще, а эксперимент. Представьте шеф-повара: он выдвигает гипотезу («суп пересолен из-за »), проводит пробу и только потом корректирует рецепт:
salt = 2 * x
print("probe:", salt)
Логи используйте для проверки ингредиентов (данных), а трассировку — когда теряетесь в последовательности этапов (потоке). Золотое правило: не исправляйте, пока не объяснили причину, иначе «магическое шаманство» вернёт баг завтра.
Чистый код отлаживать проще (лекция 12), но сейчас мы учимся находить ошибки даже в чужих спагетти-кодах. Как писать, чтобы багов изначально было меньше, — в следующей лекции.