Отладка вычислений
Неверное число без единой ошибки — самый коварный тип бага. Разберём три причины и надёжный способ их искать.
Формула, которая выдаёт неверное число, — гораздо коварнее, чем ошибка, которая роняет программу: Python не подскажет, что что-то не так. Разберём, как искать такие проблемы осознанно, а не методом «поменял и посмотрел».
1. Синтаксическая ошибка
Самый простой случай — программа не запускается вовсе, и Python указывает на конкретную строку и символ ещё до начала выполнения:
# itog = (a + b * c
# SyntaxError: '(' was never closed — не хватает закрывающей скобки
2. Ошибка в формуле
Самая опасная категория — программа выполняется без единой ошибки, но результат неверен. Обычно за этим стоит одна из трёх причин, которые мы уже разбирали в этой главе:
| Причина | Пример | Где мы это уже видели |
|---|---|---|
| забытые скобки | 4 + 7 + 9 / 3 вместо (4 + 7 + 9) / 3 | раздел «Скобки и формулы» |
| не та переменная | print(shirina + shirina) вместо print(perimetr) | раздел «Скобки и формулы» |
| перепутанный приоритет | -2 ** 2 — не то же самое, что (-2) ** 2 | раздел «Унарные операторы» |
Приём: трассировка формулы через именованные шаги
Когда одна длинная формула даёт подозрительный результат, самый надёжный способ найти проблему — разбить её на именованные промежуточные шаги и напечатать каждый:
# было — одна строка, непонятно, что пошло не так:
itog = (cena - skidka) * kolichestvo + dostavka / kolichestvo
# стало — видно каждый промежуточный результат:
cena_so_skidkoj = cena - skidka
print("Цена со скидкой:", cena_so_skidkoj)
summa_za_tovar = cena_so_skidkoj * kolichestvo
print("Сумма за товар:", summa_za_tovar)
dostavka_na_edinicu = dostavka / kolichestvo
print("Доставка на единицу:", dostavka_na_edinicu)
itog = summa_za_tovar + dostavka_na_edinicu
print("Итог:", itog)
print() обычно убирают, но иногда осмысленные имена промежуточных переменных стоит оставить — они делают код понятнее, даже если он больше не в отладке.3. Особенность представления чисел
Третья категория — код синтаксически верен, формула тоже верна, но результат всё равно удивляет. Обычно причина в том, что мы уже подробно разбирали в главе 4:
print(0.1 + 0.2 == 0.3) # False! — не баг, а особенность float
print(round(0.1 + 0.2, 10) == 0.3) # True — сравнение с допуском работает верно
== между float ведёт себя неожиданно — значит, дело в представлении чисел, а не в ошибке формулы.Предскажите, прежде чем запускать
Лучшая привычка для отладки — формировать её ещё до появления багов: прежде чем запускать код, предскажите результат. Если предсказание не совпало с реальностью — вы либо нашли баг, либо неверно понимаете код. Оба случая стоит разобрать, не запуская код дальше не глядя.
Не запуская код, определите результат: итог = 2 + 3 * 4 ** 2 // 5. Затем проверьте себя.
В коде srednyaya_ocenka = ocenka1 + ocenka2 + ocenka3 / 3 есть ошибка приоритета. Найдите и исправьте её.
Возьмите формулу расчёта итоговой суммы заказа с учётом скидки и доставки из примера выше и добавьте к ней трассировку с понятными именами промежуточных переменных.