Обычно классы отражают некоторые объекты окружающей действительности. Но иногда нам приходится работать с сущностями, которые не имеют конкретного воплощения. Например, сущность "животное". Есть конкретные животные - кошка, собака и так далее, но животное как таковое не имеет конкретного воплощения. Или сущность "геометрическая фигура". Есть прямоугольник, квадрат, круг, треугольник, но сама по себе геометрическая фигура также не имеет конкретного воплощения. И обычно для описания подобных сущностей применяются абстрактные классы.
В языке Python все инструменты для создания абстрактных классов определены в специальном модуле abc, который надо дополнительно подключать в приложении
import abc
Ключевыми компонентами этого модуля является класс ABC и аннотация @abstractmethod. Класс ABC упрощает создание абстрактного класса, и все определяемые абстрактные классы наследуются от этого класса. Аннотация @abstractmethod предназначеня для создания абстрактного метода.
Абстрактные классы определяются как обычные классы за тем исключением, что они наследуются от класса ABC из модуля abc. Например, определим абстрактный класс геометрической фигуры:
import abc
class Shape(abc.ABC):
pass
Как правило, абстрактные классы объявляют некоторый общий функционал для классов наследников. Причем некоторый функционал может не иметь никакой реализации - его
реализацию должны определить классы-наследники. Подобный функционал оформляется в классе в виде абстрактных методов.
Например, класс геометрической фигуры может иметь методы вычисления периметра, площади и т.д. Мы не можем определить общую формулу для вычисления площади всех фигур -
для каждой конкретной фигуры принцип вычисления площади может отличаться. Поэтому в классе фигуры мы можем определить метод вычисления площади как абстрактный.
Для этого применяется аннотация @abstractmethod из модуля abc:
import abc
class Shape(abc.ABC):
@abc.abstractmethod
def area (self): pass # площадь фигуры
В данном случае метод area() определен как абстрактный. Так как ему не нужен конкретный функционал, в нем вызывается оператор pass
Стоит отметить, что мы не можем напрямую создать объект абстрактного класса с абстрактными методами, используя его конструктор:
import abc
class Shape(abc.ABC):
@abc.abstractmethod
def area (self): pass # площадь фигуры
shape = Shape() # ! Ошибка - так нельзя
print(shape)
Классы-наследники должны реализовать все абстрактные методы абстрактного класса. Например, определим класс прямоугольника:
import abc
class Shape(abc.ABC):
@abc.abstractmethod
def area (self): pass # площадь фигуры
# класс прямоугольника
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area (self): return self.width * self.height
rect = Rectangle(30, 50)
print("Rectangle area:", rect.area()) # Rectangle area: 1500
Здесь класс прямоугольника Rectangle принимает через конструктор ширину и высоту и использует их для вычисления площади в методе area().
Подобным образом можно определить и другие типы фигур. Например, добавим класс круга:
import abc
class Shape(abc.ABC):
@abc.abstractmethod
def area (self): pass # площадь фигуры
# класс прямоугольника
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area (self): return self.width * self.height
# класс круга
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area (self): return self.radius * self.radius * 3.14
def print_area(shape):
print("Area:", shape.area())
rect = Rectangle(30, 50)
circle = Circle(30)
print_area(rect) # Area: 1500
print_area(circle) # Area: 2826.0
В данном случае для вывода площади фигуры определена функция print_area, которая принимает любую фигуру.
При этом абстрактные классы также могут определять конструктор, атрибуты, неабстрактные методы, которые также могут применяться в классах-наследниках:
import abc
class Shape(abc.ABC):
def __init__(self, x, y):
self.x = x
self.y = y
@abc.abstractmethod
def area (self): pass # абстрактны метод
def print_point(self): # неабстрактный метод
print("X:", self.x, "\tY:", self.y)
# класс прямоугольника
class Rectangle(Shape):
def __init__(self, x, y, width, height):
super().__init__(x, y)
self.width = width
self.height = height
def area (self): return self.width * self.height
rect = Rectangle(10, 20, 100, 100)
rect.print_point() # X: 10 Y: 20
Здесь абстрактный класс Shape через конструктор принимает координаты X и Y для точки, относительно которой создается фигура (например, для прямоугольника это могут быть координаты верхнего левого угла, для круга - центр). И также определен неабстрактный метод print_point, который выводит координаты точки на консоль.