🏠 Домашнее задание

⚡ Суть задания

Три задачи на закрепление замыканий: фабрика округления, логгер событий со списком и декоратор рамки вокруг вывода.

Результат: три рабочих скрипта, которые можно запустить в VS Code.

📋 Задание 1. Фабрика функций округления

Исходное задание из LMS:

Создайте функцию make_rounder(), которая принимает количество знаков для округления и возвращает другую функцию. Полученная функция должна принимать число и возвращать его, округлённое до указанного ранее количества знаков после запятой.

Пример вызова:

print(round2(3.14159))
print(round2(2.71828))
print(round0(9.999))

Пример вывода:

3.14
2.72
10.0

Шаг 1. Определите внешнюю функцию

Внешняя функция принимает количество знаков после запятой и возвращает вложенную функцию.

def make_rounder(precision: int):
    def round_number(value: float) -> float:
        return round(value, precision)

    return round_number

Шаг 2. Создайте конкретные функции

round2 = make_rounder(2)
round0 = make_rounder(0)

Шаг 3. Проверьте результат

print(round2(3.14159))  # 3.14
print(round2(2.71828))  # 2.72
print(round0(9.999))    # 10.0

✅ Полное решение

def make_rounder(precision: int):
    """Factory that returns a rounding function."""
    def round_number(value: float) -> float:
        return round(value, precision)

    return round_number


if __name__ == "__main__":
    round2 = make_rounder(2)
    round0 = make_rounder(0)

    print(round2(3.14159))  # 3.14
    print(round2(2.71828))  # 2.72
    print(round0(9.999))    # 10.0

📋 Задание 2. Расширяемый логгер событий

Исходное задание из LMS:

Создайте функцию, которая возвращает вложенный логгер событий. Каждый вызов логгера должен сохранять событие с текущим временем (если оно передано) и возвращать весь список событий.

Пример вызова:

log("Загрузка данных")
log("Обработка завершена")
log("Сохранение файла")
for event in log():
    print(event)

Пример вывода:

Загрузка данных: 2025-03-24 14:06:29
Обработка завершена: 2025-03-24 14:06:29
Сохранение файла: 2025-03-24 14:06:29

Шаг 1. Создайте фабрику логгера

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

from datetime import datetime


def make_event_logger():
    events: list[str] = []

    def log(message: str | None = None) -> list[str]:
        if message is not None:
            timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            events.append(f"{message}: {timestamp}")
        return events

    return log

Шаг 2. Используйте логгер

log = make_event_logger()
log("Загрузка данных")
log("Обработка завершена")
log("Сохранение файла")

for event in log():
    print(event)

✅ Полное решение

from datetime import datetime


def make_event_logger():
    """Returns a logger that keeps all events in a closure."""
    events: list[str] = []

    def log(message: str | None = None) -> list[str]:
        if message is not None:
            timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            events.append(f"{message}: {timestamp}")
        return events

    return log


if __name__ == "__main__":
    log = make_event_logger()
    log("Загрузка данных")
    log("Обработка завершена")
    log("Сохранение файла")

    for event in log():
        print(event)

📋 Задание 3. Рамка вокруг вывода

Исходное задание из LMS:

Создайте декоратор frame, который оборачивает результат функции рамкой из 50 символов -, выводя по строке до и после вызова функции.

Пример декорируемой функции:

@frame
def say_hello():
    print("Привет, игрок!")

Пример вывода:

--------------------------------------------------
Привет, игрок!
--------------------------------------------------

Шаг 1. Создайте декоратор

Декоратор возвращает вложенную функцию, которая печатает рамку до и после вызова исходной функции.

def frame(func):
    border = "-" * 50

    def wrapper() -> None:
        print(border)
        func()
        print(border)

    return wrapper

Шаг 2. Примените декоратор

@frame
def say_hello():
    print("Привет, игрок!")


say_hello()

✅ Полное решение

def frame(func):
    """Decorator that prints a frame around function output."""
    border = "-" * 50

    def wrapper() -> None:
        print(border)
        func()
        print(border)

    return wrapper


@frame
def say_hello():
    print("Привет, игрок!")


if __name__ == "__main__":
    say_hello()

🔧 Подготовка окружения

  1. Создайте папку python-fundamentals-practice/lesson-62/.
  2. Создайте файлы homework_01_rounder.py, homework_02_logger.py и homework_03_frame.py.
  3. Скопируйте в каждый файл соответствующее полное решение.

🧪 Как проверить решение

  1. Активируйте venv: venv\Scripts\activate (Windows) или source venv/bin/activate (Mac/Linux).
  2. Запустите скрипты:
    • python homework_01_rounder.py
    • python homework_02_logger.py
    • python homework_03_frame.py
  3. Убедитесь, что вывод совпадает с примерами из заданий.

💡 Советы и типичные ошибки

  • В задании 2 список events должен быть в замыкании, а не в глобальной переменной или значением по умолчанию.
  • В задании 3 рамка печатается в декораторе, а не возвращается строкой.
  • Если в задании 1 передать отрицательное значение precision, функция round округлит до десятков/сотен. Для учебной задачи это допустимо, но в production стоит добавить проверку.