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

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

Создать абстрактный класс Shape и две фигуры — Circle и Rectangle. Добавить пользовательское исключение InvalidSizeError для проверки размеров.

Результат: файл shapes.py, который корректно считает площади и выбрасывает InvalidSizeError при некорректных размерах.

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

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

Фигуры и площади. Создайте абстрактный класс Shape. В классе должен быть метод area(), который возвращает площадь фигуры. Реализуйте два класса:

  • Circle, который принимает радиус.
  • Rectangle, который принимает ширину и высоту.
# Пример использования
shapes = [Circle(3), Rectangle(4, 5)]
for shape in shapes:
    print(f"Area: {shape.area():.2f}")

Проверка размеров фигур. Доработайте фигуры:

  • Добавьте проверку в конструкторы Circle и Rectangle, чтобы значения были положительными.
  • Если передано отрицательное или нулевое значение, выбрасывайте пользовательское исключение InvalidSizeError.

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

Это задание использует только стандартную библиотеку Python, поэтому дополнительные пакеты устанавливать не нужно.

📁 Структура репозитория: мы используем один репозиторий для всех практических работ курса. Это удобнее, чем 88 отдельных репозиториев: общий .gitignore, единая история, легко переключаться между уроками.
1

Создайте (или откройте) рабочую папку

В VS Code нажмите Ctrl+K Ctrl+O → выберите или создайте папку python-fundamentals-practice. Это будет корень вашего учебного репозитория.

# Или в терминале VS Code (Ctrl + `):
mkdir python-fundamentals-practice
cd python-fundamentals-practice
2

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

Если папка ещё не является git-репозиторием:

git init

# Создайте базовые файлы
echo "# Python Fundamentals — Практические работы" > README.md
echo "venv/" > .gitignore
echo "__pycache__/" >> .gitignore
echo "*.pyc" >> .gitignore
echo ".env" >> .gitignore

git add README.md .gitignore
git commit -m "init: базовая структура репозитория"
3

Создайте папку для текущего урока

# Структура: oop/lesson-76/
mkdir -p oop/lesson-76
cd oop/lesson-76
4

Создайте виртуальное окружение (venv)

# Windows (PowerShell / CMD)
python -m venv venv
venv\Scripts\activate

# Mac / Linux
python3 -m venv venv
source venv/bin/activate

После активации в начале строки терминала появится (venv). Все дальнейшие команды выполняйте внутри venv.

5

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

# Вернитесь в корень репозитория
cd ../../..

# Создайте и переключитесь на ветку
git checkout -b lesson/76-abstract-classes-exceptions

# Отправьте ветку на удалённый репозиторий (если настроен)
git push -u origin lesson/76-abstract-classes-exceptions

Именование веток: lesson/XX-краткое-название. Это позволяет легко находить нужный урок.

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

1

Создайте файл shapes.py

В папке oop/lesson-76/ создайте файл shapes.py.

import math
from abc import ABC, abstractmethod


class InvalidSizeError(ValueError):
    """Raised when a shape dimension is not positive."""
    pass


class Shape(ABC):
    """Abstract base class for geometric shapes."""

    @abstractmethod
    def area(self) -> float:
        """Return the area of the shape."""
        pass


class Circle(Shape):
    """A circle defined by its radius."""

    def __init__(self, radius: float) -> None:
        if radius <= 0:
            raise InvalidSizeError(f"Radius must be positive, got {radius}")
        self.radius = radius

    def area(self) -> float:
        return math.pi * self.radius ** 2


class Rectangle(Shape):
    """A rectangle defined by width and height."""

    def __init__(self, width: float, height: float) -> None:
        if width <= 0 or height <= 0:
            raise InvalidSizeError(
                f"Width and height must be positive, got {width}x{height}"
            )
        self.width = width
        self.height = height

    def area(self) -> float:
        return self.width * self.height


if __name__ == "__main__":
    shapes = [Circle(3), Rectangle(4, 5)]
    for shape in shapes:
        print(f"Area: {shape.area():.2f}")

    # Проверка ошибки
    try:
        Circle(-1)
    except InvalidSizeError as e:
        print("Caught InvalidSizeError:", e)

Проверка в VS Code:

  1. Убедитесь, что venv активирован.
  2. Запустите: python oop/lesson-76/shapes.py.
  3. В терминале должны появиться площади фигур и сообщение об ошибке.
2

Проверьте, что нельзя создать экземпляр Shape

>>> from shapes import Shape
>>> Shape()
TypeError: Can't instantiate abstract class Shape with abstract method area

Это подтверждает, что Shape действительно абстрактный.

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

Полное решение приведено в шаге 1. Ниже краткая версия без комментариев:

import math
from abc import ABC, abstractmethod


class InvalidSizeError(ValueError):
    pass


class Shape(ABC):
    @abstractmethod
    def area(self) -> float:
        pass


class Circle(Shape):
    def __init__(self, radius: float) -> None:
        if radius <= 0:
            raise InvalidSizeError(f"Radius must be positive, got {radius}")
        self.radius = radius

    def area(self) -> float:
        return math.pi * self.radius ** 2


class Rectangle(Shape):
    def __init__(self, width: float, height: float) -> None:
        if width <= 0 or height <= 0:
            raise InvalidSizeError(f"Width and height must be positive, got {width}x{height}")
        self.width = width
        self.height = height

    def area(self) -> float:
        return self.width * self.height


shapes = [Circle(3), Rectangle(4, 5)]
for shape in shapes:
    print(f"Area: {shape.area():.2f}")

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

  1. Активируйте venv: venv\Scripts\activate (Windows) или source venv/bin/activate (Mac/Linux).
  2. Запустите файл: python oop/lesson-76/shapes.py.
  3. Проверьте вывод в терминале:
    • Area: 28.27 (площадь круга с радиусом 3).
    • Area: 20.00 (площадь прямоугольника 4×5).
    • Сообщение об InvalidSizeError для Circle(-1).
  4. Убедитесь, что Shape() вызывает TypeError.

🐛 Отладка в VS Code

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

  1. Поставьте точку останова (breakpoint) в методе __init__ или area.
  2. Нажмите F5 или выберите «Run → Start Debugging».
  3. Используйте панель Debug (переменные, стек вызовов, консоль).
  4. Проверьте Terminal на наличие traceback — Python показывает номер строки с ошибкой.

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

# Добавьте файлы урока
git add oop/lesson-76/

# Сделайте коммит с осмысленным сообщением
git commit -m "lesson-76: абстрактные классы и пользовательские исключения"

# Отправьте на GitHub / GitLab
git push

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

  • Не забудьте from abc import ABC, abstractmethod и наследование Shape(ABC).
  • Вызывайте super().__init__(...) в пользовательских исключениях, если переопределяете __init__.
  • Для числа π используйте math.pi, а не приближённые значения вроде 3.14.
  • Наследуйте InvalidSizeError от ValueError, если ошибка связана с некорректным значением.