Решение задания 1. Обработка ввода пользователя
# solution_01_input.py
def get_valid_number() -> float:
"""Запрашивает у пользователя число до тех пор, пока ввод не станет корректным."""
while True:
try:
return float(input("Введите число: "))
except ValueError:
print("Ошибка: Введите корректное число.")
number = get_valid_number()
print(f"Вы ввели число: {number}")
Логика: функция бесконечно запрашивает ввод. Если float() не может преобразовать строку, перехватывается ValueError, выводится сообщение, и цикл продолжается. При успехе функция возвращает число, прерывая цикл.
Решение задания 2. Проверка возраста
# solution_02_age.py
def check_age(age: int) -> None:
"""Проверяет, что возраст не меньше 18 лет."""
if age < 18:
raise ValueError("Возраст должен быть 18 лет и старше.")
print("Возраст принят.")
try:
user_age = int(input("Введите возраст: "))
check_age(user_age)
except ValueError as e:
print(f"Ошибка: {e}")
Логика: guard clause отсекает недопустимый возраст. Исключение перехватывается в основной программе и выводится понятное сообщение.
Решение задания 3. Безопасное деление
# solution_03_divide.py
from typing import Optional
def safe_divide(dividend: str, divisor: str) -> Optional[float]:
"""Делит два числа, переданных в виде строк, и обрабатывает ошибки."""
try:
a = float(dividend)
b = float(divisor)
return a / b
except ValueError:
print("Ошибка: Введено некорректное число.")
except ZeroDivisionError:
print("Ошибка: Деление на ноль.")
return None
dividend_input = input("Введите делимое: ")
divisor_input = input("Введите делитель: ")
result = safe_divide(dividend_input, divisor_input)
if result is not None:
print(f"Результат: {result}")
Логика: преобразование строк в float может вызвать ValueError, а деление на ноль — ZeroDivisionError. Каждая ошибка обрабатывается отдельным обработчиком. При ошибке функция возвращает None.
Решение задания 4. Пользовательское исключение
# solution_04_bank.py
class InsufficientFundsError(Exception):
"""Ошибка: недостаточно средств на счёте."""
def __init__(self, balance: float, amount: float):
self.balance = balance
self.amount = amount
super().__init__(
f"Недостаточно средств: баланс {balance}, требуется {amount}"
)
def withdraw(balance: float, amount: float) -> float:
"""Снимает amount со счёта balance."""
if amount > balance:
raise InsufficientFundsError(balance, amount)
return balance - amount
try:
new_balance = withdraw(100.0, 150.0)
print(f"Остаток: {new_balance}")
except InsufficientFundsError as e:
print(f"Ошибка: {e}")
Логика: класс исключения хранит баланс и запрашиваемую сумму, формируя понятное сообщение. Функция withdraw возбуждает исключение при недостатке средств.
Решение задания 5. Логирование ошибок
# solution_05_logged_divide.py
import logging
from typing import Optional
logging.basicConfig(
filename="errors.log",
format="%(asctime)s - %(levelname)s - %(filename)s - %(lineno)d - %(message)s",
level=logging.ERROR,
)
def safe_divide(dividend: str, divisor: str) -> Optional[float]:
try:
a = float(dividend)
b = float(divisor)
return a / b
except ValueError:
logging.error("Ошибка: Введено некорректное число.")
print("Ошибка: Введено некорректное число.")
except ZeroDivisionError:
logging.error("Ошибка: Деление на ноль.")
print("Ошибка: Деление на ноль.")
return None
dividend_input = input("Введите делимое: ")
divisor_input = input("Введите делитель: ")
result = safe_divide(dividend_input, divisor_input)
if result is not None:
print(f"Результат: {result}")
Логика: настройка logging.basicConfig направляет сообщения уровня ERROR в файл. Формат включает время, уровень, имя файла, номер строки и текст ошибки.
Решение задания 6. Использование assert
# solution_06_assert.py
def apply_discount(price: float, discount: float) -> float:
"""Применяет скидку к цене."""
assert price >= 0, "Цена не может быть отрицательной"
assert 0 <= discount <= 1, "Скидка должна быть в диапазоне [0, 1]"
return price * (1 - discount)
print(apply_discount(100.0, 0.2)) # 80.0
# apply_discount(100.0, 1.5) # AssertionError
Логика: assert проверяет внутренние инварианты функции. Если условие нарушено, возникает AssertionError с поясняющим сообщением.