Menu
Coddy logo textTech

Абстрактные классы и методы

Часть раздела Object Oriented Programming путешествия по Python на Coddy — урок 25 из 64.

Абстрактные классы — это классы, экземпляры которых нельзя создать напрямую, и которые содержат абстрактные методы, обязательные для реализации в подклассах.

Представьте это в виде формы: Абстрактный класс похож на шаблон обязательной формы, где некоторые поля обязательны для заполнения (абстрактные методы), а некоторые — нет (конкретные методы). Вы не можете отправить саму форму — сначала нужно заполнить все обязательные поля. Аналогично, вы не можете создавать экземпляры абстрактного класса до тех пор, пока все абстрактные методы не будут реализованы в подклассе.

Импортируйте модуль abc для создания абстрактных классов:

from abc import ABC, abstractmethod

Создайте абстрактный класс с абстрактными методами:

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass
    
    @abstractmethod
    def perimeter(self):
        pass
    
    def describe(self):
        return "This is a shape"  # Конкретный метод (имеет реализацию)

Декоратор @abstractmethod помечает методы, которые обязательно должны быть реализованы подклассами — это «обязательные поля» в вашей форме. Обычные методы, такие как describe(), могут иметь реализации и наследуются как есть — это «предварительно заполненные поля».

Зачем использовать абстрактные классы? Они обеспечивают соблюдение контракта. Если вы создаете класс Shape, вы утверждаете, что «каждая фигура ДОЛЖНА уметь вычислять свою площадь и периметр». Это предотвращает ошибки, при которых вы можете забыть реализовать критически важные методы.

Попробуйте создать экземпляр абстрактного класса:

# Это вызовет ошибку:
# shape = Shape()  # TypeError: Can't instantiate abstract class

Эта ошибка преднамеренна — это всё равно что пытаться отправить пустую форму. Python говорит: «Вы еще не заполнили обязательные поля (методы area и perimeter)!»

Создайте конкретный подкласс, который реализует все абстрактные методы:

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius
    
    def area(self):
        return 3.14 * self.radius ** 2
    
    def perimeter(self):
        return 2 * 3.14 * self.radius

Теперь мы «заполнили все обязательные поля» — мы реализовали как area(), так и perimeter(). Класс Circle теперь является полным, конкретным классом, экземпляр которого можно создать.

Теперь вы можете создавать экземпляры конкретного класса:

circle = Circle(5)
print(circle.area())
print(circle.perimeter())
print(circle.describe())  # Наследуемый конкретный метод

Создайте еще один конкретный подкласс:

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    def area(self):
        return self.width  self.height
    
    def perimeter(self):
        return 2 * (self.width + self.height)

rectangle = Rectangle(4, 6)
print(rectangle.area())
print(rectangle.perimeter())

Вывод:

78.5
31.400000000000002
This is a shape
24
20

Абстрактные классы определяют контракт, которому должны следовать подклассы. Используйте ABC и @abstractmethod для создания абстрактных классов. Подклассы должны реализовать все абстрактные методы, иначе они также будут абстрактными. Это обеспечивает согласованность и предотвращает неполные реализации.

challenge icon

Задание

Средне

В этом испытании вы реализуете систему обработки платежей.

Вам необходимо завершить реализацию в следующих файлах:

  • paymentmethod.py — абстрактный базовый класс с логикой валидации
  • creditcard.py — реализация оплаты кредитной картой
  • paypal.py — реализация оплаты через PayPal

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

Файл driver.py содержит тестовые случаи, которые проверят правильность вашей реализации, обрабатывая различные сценарии оплаты и отображая соответствующие детали для каждого метода оплаты.

Шпаргалка

Абстрактные классы не могут быть инстанцированы напрямую и содержат абстрактные методы, которые должны быть реализованы подклассами.

Импортируйте модуль abc:

from abc import ABC, abstractmethod

Создайте абстрактный класс с абстрактными методами:

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass
    
    @abstractmethod
    def perimeter(self):
        pass
    
    def describe(self):
        return "This is a shape"  # Конкретный метод (имеет реализацию)

Декоратор @abstractmethod помечает методы, которые должны быть реализованы подклассами. Обычные методы могут иметь реализации.

Абстрактные классы не могут быть инстанцированы:

# Это вызовет ошибку:
# shape = Shape()  # TypeError: Can't instantiate abstract class

Создайте конкретные подклассы, которые реализуют все абстрактные методы:

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius
    
    def area(self):
        return 3.14 * self.radius ** 2
    
    def perimeter(self):
        return 2 * 3.14 * self.radius

circle = Circle(5)
print(circle.area())        # 78.5
print(circle.describe())    # Унаследованный конкретный метод

Ключевой момент: Абстрактные классы определяют шаблон, которому должны следовать подклассы. Подклассы должны реализовать все абстрактные методы, иначе они также будут абстрактными.

Попробуйте сами

from creditcard import CreditCard
from paypal import PayPal

# Тестирование реализаций - НЕ ИЗМЕНЯЙТЕ ЭТОТ ТЕСТОВЫЙ КОД
cc = CreditCard("1234567890123456")
pp = PayPal("user@example.com")

# Обработка корректных платежей
if cc.validate(100):
    print(cc.process_payment(100))
    print(cc.payment_details())

if pp.validate(200):
    print(pp.process_payment(200))
    print(pp.payment_details())

# Попытка оплаты с некорректной суммой
if not pp.validate(0):
    print("Invalid payment amount")

# Это вызвало бы ошибку, если бы было раскомментировано:
# pm = PaymentMethod()  # Невозможно создать экземпляр абстрактного класса
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Object Oriented Programming