Глава 4 · Python любит числа

Числовые ошибки и отладка

Девять коротких лабораторий — от настоящих исключений до валидных, но неожиданных числовых результатов.

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

Что пошло не так?
Python остановился с исключением
ZeroDivisionError
ValueError
TypeError
Читаем traceback и исправляем причину
Код выполнился без исключения
Но результат выглядит неожиданно
Проверяем математическую модель и представление чисел
Исправляем код, ожидания или выбор числового типа
Лаборатория 1. Почему 10 / 2 даёт 5.0?
предскажите и запустите
print(10 / 2)

Наблюдение: результат — 5.0, а не 5.

Объяснение: оператор / в Python ВСЕГДА возвращает float (раздел 4.7), даже при делении нацело.

Лаборатория 2. Почему int(3.99) равно 3?
предскажите и запустите
print(int(3.99))

Наблюдение: результат — 3, а не 4.

Объяснение: int() отбрасывает дробную часть (усечение к нулю), а не округляет (раздел 4.16).

Лаборатория 3. Почему сравнение 0.1 + 0.2 == 0.3 не срабатывает?
предскажите и запустите
print(0.1 + 0.2 == 0.3)

Наблюдение: результат — False.

Объяснение: float хранит приближение (раздел 4.11) — используйте math.isclose() вместо ==.

Лаборатория 4. Почему Decimal(0.1) выглядит странно?
предскажите и запустите
from decimal import Decimal
print(Decimal(0.1))

Наблюдение: результат — длинная некруглая дробь, а не 0.1.

Объяснение: аргумент — float, а значит приближение из раздела 4.11 уже произошло ДО создания Decimal. Используйте Decimal("0.1").

Лаборатория 5. Почему 7 // 3 отличается от 7 / 3?
предскажите и запустите
print(7 // 3)
print(7 / 3)

Наблюдение: 2 против 2.333....

Объяснение: // — целочисленное деление (отбрасывает дробную часть результата), / — обычное деление, всегда float.

Лаборатория 6. Предскажите: -7 // 3 и -7 % 3
предскажите и запустите
print(-7 // 3)
print(-7 % 3)

Наблюдение: -3 и 2 — не то, что многие ожидают.

Объяснение: // округляет к минус бесконечности, а не к нулю (раздел 4.7).

Лаборатория 7. Исправьте: int("FF", 16)
предскажите и запустите
print(int("FF"))

Наблюдение: ValueError: invalid literal for int() with base 10: 'FF'

Объяснение: без указания основания int() ожидает десятичную запись. Нужно: int("FF", 16).

Лаборатория 8. Почините ZeroDivisionError
предскажите и запустите
price = 100
quantity = 0
print(price / quantity)

Наблюдение: ZeroDivisionError: division by zero.

Объяснение: перед делением стоит проверить делитель на ноль, если он может быть нулевым — подробно про условия будет в следующей главе; здесь достаточно узнать сам тип ошибки.

Лаборатория 9. Выберите верный числовой тип
предскажите и запустите
# Задача: посчитать точную сумму чека в рублях и копейках

Наблюдение: float способен дать погрешность в дробных суммах.

Объяснение: для денег правильный выбор — Decimal (раздел 4.14), а не float.

Практика: отладка числовых ошибок
Интерактивный ноутбук прямо в браузере — найдите и исправьте типичные числовые проблемы
Открыть практику →