При проектировании сложных систем разработчикам часто нужно определить структуру и поведение классов, которые не предназначены для создания экземпляров, а служат только основой для наследования. Как же гарантировать, что эти классы не будут использованы напрямую, и как обозначить методы, которые обязательно должны быть реализованы в наследниках? Здесь на помощь приходят абстрактные классы.
В этом уроке мы рассмотрим, что такое абстрактные классы и как они помогают решить вышеуказанные проблемы.
Что такое абстрактные классы
В Python есть понятие абстрактного класса. Это класс, который не может быть инстанциирован напрямую — только через его наследников. Но во всем остальном, это обычный класс со своими методами и атрибутами:
from abc import ABC, abstractmethod
class HTMLElement(ABC):
def __init__(self, attributes={}):
self.attributes = attributes
def get_attribute(self, key):
return self.attributes.get(key)
В данном примере мы создаем абстрактный класс HTMLElement
с помощью модуля abc
. Этот класс содержит метод get_attribute
, который возвращает значение атрибута элемента.
Абстрактные классы имеют смысл только в связке с наследованием. Они подразумевают, что от базового класса не будут создаваться объекты, поэтому его логично пометить как абстрактный.
Особенность абстрактных классов в Python связана с реализацией интерфейсов. В отличие от обычного класса, абстрактный класс не обязан реализовывать все методы интерфейса — это должны сделать его наследники:
from abc import ABC, abstractmethod
class Showable(ABC):
@abstractmethod
def __str__(self):
pass
В этом примере мы создаем абстрактный класс Showable
с абстрактным методом __str__
. Этот метод должен быть реализован в любом классе, который наследует Showable
.
Так каждый наследник класса HTMLElement
должен реализовывать метод __str__()
:
class HTMLElement(Showable):
def __init__(self, attributes={}):
self.attributes = attributes
def get_attribute(self, key):
return self.attributes.get(key)
def __str__(self):
# реализация метода __str__ для HTMLElement
return f"HTMLElement with attributes: {self.attributes}"
В данном случае HTMLElement
наследуется от Showable
, что означает, что каждый класс, производный от HTMLElement
, должен реализовывать метод __str__()
.
Если класс наследник не реализует обязательный метод __str__()
, то при попытке создания экземпляра этого класса Python выдаст ошибку.
Теперь подробнее рассмотрим, когда стоит использовать абстрактные классы, а когда нет, и как их совмещать с ООП.
Когда использовать абстрактные классы
С появлением абстрактных классов возникает множество новых вопросов: когда они нужны, а когда нет, можно ли использовать абстрактные классы вместо интерфейсов, как совмещать их с интерфейсами и так далее.
Абстрактные классы — не фундаментальная концепция и не обязательная часть ООП. Более того, в большинстве ООП-языков абстрактных классов нет. Однако нельзя сказать, что код на них пишется хуже. Не стоит придавать большое значение абстрактным классам. Их нужно использовать, когда класс имеет смысл пометить абстрактным, но не более того.
Выводы
Абстрактные классы и абстрактные методы могут быть полезными инструментами при проектировании структуры кода. Они позволяют явно указать, какие методы должны быть реализованы в классах-потомках. Но важно помнить, что они добавляют определенный уровень сложности и их следует использовать осознанно.
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.