Обычно классы отражают некоторые объекты окружающей действительности. Но иногда нам приходится работать с сущностями, которые не имеют конкретного воплощения. Например, сущность "животное". Есть конкретные животные - кошка, собака и так далее, но животное как таковое не имеет конкретного воплощения. Или сущность "геометрическая фигура". Есть прямоугольник, квадрат, круг, треугольник, но сама по себе геометрическая фигура также не имеет конкретного воплощения. И обычно для описания подобных сущностей применяются абстрактные классы.
В языке 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, который выводит координаты точки на консоль.