✅ Решения заданий

⚡ Решения в двух словах

  • 1 — цикл с float() и ValueError.
  • 2 — guard clause + raise ValueError.
  • 3 — обработка ValueError и ZeroDivisionError.
  • 4 — класс исключения + проверка баланса.
  • 5 — добавить logging к решению 3.
  • 6 — assert для цены и скидки.
⚠️ Рекомендация: попробуйте решить задания самостоятельно, прежде чем смотреть ответы. Решения — это последняя инстанция, когда собственные попытки исчерпаны.

Решение задания 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 с поясняющим сообщением.