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

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

Домашнее задание совпадает с пятью практическими задачами урока. Результат — файл homework_49.py, в котором все функции реализованы и запускаются из if __name__ == "__main__":.

Пошаговый план: создать репозиторий, настроить venv, создать файл, скопировать решение, запустить и проверить вывод.

📋 Формулировка задания

Для этого практического урока нет отдельного задания в LMS, поэтому домашним заданием становятся сами пять задач урока:

  1. Сумма чисел списка — рекурсивная функция с валидацией типов.
  2. Реверс строки — рекурсивная функция с проверкой строкового типа.
  3. Глубина вложенности списка — рекурсивный подсчёт глубины.
  4. Сумма продаж — рекурсивный обход дерева подразделений.
  5. Читабельный формат словаря — рекурсивное «выпрямление» вложенного словаря.

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

Эти шаги одинаковы для всех уроков курса.

1

Создайте рабочую папку

mkdir python-fundamentals-practice
cd python-fundamentals-practice
2

Инициализируйте Git-репозиторий (один раз)

git init
echo "# Python Fundamentals — Практические работы" > README.md
echo "venv/" > .gitignore
echo "__pycache__/" >> .gitignore
echo "*.pyc" >> .gitignore
echo ".env" >> .gitignore
echo "*.log" >> .gitignore
git add README.md .gitignore
git commit -m "init: базовая структура репозитория"
3

Создайте папку для урока и виртуальное окружение

mkdir lesson-49
cd lesson-49
python -m venv venv

# Windows
venv\Scripts\activate

# Mac / Linux
source venv/bin/activate
4

Создайте ветку для задания

cd ..
git checkout -b lesson/49-practice-12

🛠️ Шаги выполнения задания

1

Шаг 1: создайте файл homework_49.py

В папке lesson-49 создайте файл и реализуйте пять функций.

2

Шаг 2: добавьте валидацию через isinstance

Перед рекурсивной обработкой проверяйте тип аргумента. При ошибке возбуждайте TypeError с понятным сообщением.

3

Шаг 3: обработайте исключения при вызове

Каждый вызов функции в блоке if __name__ == "__main__": оберните в try/except, чтобы программа не прерывалась.

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

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

"""Lesson 49 practice: recursion + validation with exceptions."""


def recursive_sum(numbers: list) -> float:
    """Return the sum of all numbers in a list recursively."""
    if not isinstance(numbers, list):
        raise TypeError("Argument must be a list")
    if not numbers:
        return 0

    head = numbers[0]
    if not isinstance(head, (int, float)):
        raise TypeError(f"All elements must be numbers, got {type(head).__name__}")
    return head + recursive_sum(numbers[1:])


def reverse_string(text: str) -> str:
    """Return the reversed string using recursion."""
    if not isinstance(text, str):
        raise TypeError("Argument must be a string")
    if len(text) <= 1:
        return text
    return text[-1] + reverse_string(text[:-1])


def list_depth(nested_list: list) -> int:
    """Return the maximum nesting depth of a list."""
    if not isinstance(nested_list, list):
        raise TypeError("Argument must be a list")

    max_inner = 0
    for item in nested_list:
        if isinstance(item, list):
            max_inner = max(max_inner, list_depth(item))
    return 1 + max_inner


def total_sales(department: dict) -> int | float:
    """Return the total sales of a department and all its sub-departments."""
    if not isinstance(department, dict):
        raise TypeError("Department must be a dict")

    sales = department.get("sales", 0)
    total = sales
    sub_departments = department.get("sub_departments")

    if sub_departments is not None:
        if not isinstance(sub_departments, list):
            raise TypeError("sub_departments must be a list")
        for sub in sub_departments:
            if not isinstance(sub, dict):
                raise TypeError("Each sub_department must be a dict")
            total += total_sales(sub)

    return total


def flatten_dict(data: dict, prefix: str = "") -> dict:
    """Convert a nested dict into a flat dict with dot-separated keys."""
    if not isinstance(data, dict):
        raise TypeError("Argument must be a dict")

    result = {}
    for key, value in data.items():
        full_key = f"{prefix}.{key}" if prefix else key
        if isinstance(value, dict):
            result.update(flatten_dict(value, full_key))
        else:
            result[full_key] = value
    return result


if __name__ == "__main__":
    # Task 1
    numbers = [1, 2, 3, 4, 5]
    try:
        print(recursive_sum(numbers))
    except (TypeError, ValueError) as e:
        print(f"Task 1 error: {e}")

    # Task 2
    text = "recursion"
    try:
        print(reverse_string(text))
    except TypeError as e:
        print(f"Task 2 error: {e}")

    # Task 3
    nested_list = [1, [2, [3, [4, [5]]]], 6, [[7, 8], 9]]
    try:
        print("Максимальная глубина:", list_depth(nested_list))
    except TypeError as e:
        print(f"Task 3 error: {e}")

    # Task 4
    company_structure = {
        "dept_name": "Head Office",
        "sales": 100,
        "sub_departments": [
            {
                "dept_name": "Sales Department",
                "sales": 200,
                "sub_departments": [
                    {"dept_name": "B2B Sales", "sales": 120}
                ]
            },
            {
                "dept_name": "IT Department",
                "sales": 150,
                "sub_departments": [
                    {
                        "dept_name": "DevOps",
                        "sales": 300,
                        "sub_departments": [
                            {"dept_name": "Cloud Infrastructure", "sales": 180}
                        ]
                    },
                    {"dept_name": "QA Department", "sales": 90}
                ]
            }
        ]
    }
    try:
        print("Общая сумма продаж:", total_sales(company_structure))
    except TypeError as e:
        print(f"Task 4 error: {e}")

    # Task 5
    data = {
        "user": {
            "id": 123,
            "info": {
                "name": "Alice",
                "location": {
                    "city": "Berlin",
                    "coordinates": {"lat": 52.52, "lon": 13.405}
                },
                "hobby": ["swimming", "drawing"]
            }
        },
        "score": 95
    }
    flat = flatten_dict(data)
    print("Данные для анализа:")
    for key, value in flat.items():
        print(f"{key} : {value}")

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

  1. Активируйте venv: venv\Scripts\activate (Windows) или source venv/bin/activate (Mac/Linux).
  2. Запустите файл: python homework_49.py.
  3. Проверьте вывод:
    • 15
    • noisrucer
    • Максимальная глубина: 5
    • Общая сумма продаж: 1140
    • Плоский словарь с ключами user.id, score и т.д.

🐛 Отладка в VS Code

Если что-то не работает:

  1. Поставьте точку останова на строке с базовым случаем рекурсии.
  2. Нажмите F5 или выберите «Run → Start Debugging».
  3. На панели Debug следите за стеком вызовов и значениями аргументов.
  4. Проверьте, что все функции возвращают значения в базовых случаях.

💾 Сохранение работы

git add lesson-49/
git commit -m "lesson-49: recursion and exception validation"
git push

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

  • Не забывайте базовый случай рекурсии.
  • Проверяйте тип через isinstance, а не type().
  • Не используйте «голый» except: — указывайте конкретные исключения.
  • Проверяйте аргументы в начале функции, а не внутри рекурсивного шага.