Ответ
Абстрактные базовые классы (Abstract Base Classes, ABC) из модуля abc
позволяют определять "контракты" для дочерних классов, обязывая их реализовывать определенные методы.
Преимущества
- Гарантия реализации контракта: С помощью декоратора
@abstractmethod
можно указать, какие методы обязательны для реализации в подклассах. Попытка создать экземпляр дочернего класса без реализации этих методов вызоветTypeError
. - Явное наследование: Иерархия классов становится очевидной и легко отслеживаемой. Проверка
isinstance()
иissubclass()
работает ожидаемо. - Запрет на создание экземпляров: Нельзя создать объект самого абстрактного класса, что логично, так как он представляет собой неполную реализацию.
Недостатки
- Жесткая иерархия наследования: Класс должен явно наследоваться от ABC, что создает сильную связь. Это может быть неудобно, если класс уже является частью другой иерархии или если вы не можете изменять его исходный код.
- Более многословный синтаксис: Требуется импортировать
ABC
иabstractmethod
, а также явно указывать метакласс.
Пример
from abc import ABC, abstractmethod
# Абстрактный класс определяет интерфейс "Хранилище"
class BaseStorage(ABC):
@abstractmethod
def save(self, data: dict):
"""Сохраняет данные."""
pass
@abstractmethod
def load(self, item_id: str) -> dict:
"""Загружает данные."""
pass
# Конкретная реализация интерфейса
class FileStorage(BaseStorage):
def save(self, data: dict):
print(f"Сохранение {data} в файл...")
def load(self, item_id: str) -> dict:
print(f"Загрузка {item_id} из файла...")
return {"id": item_id, "data": "some_data"}
# Попытка создать экземпляр без реализации метода вызовет ошибку
class IncompleteStorage(BaseStorage):
def save(self, data: dict):
pass
# Метод load() не реализован
# fs = IncompleteStorage() # TypeError: Can't instantiate abstract class ...
fs = FileStorage()
fs.save({"id": "123"})
Альтернатива: Protocol
В современном Python для определения интерфейсов часто предпочитают typing.Protocol
. Он реализует структурную типизацию (duck typing), проверяя наличие нужных методов без явного наследования, что обеспечивает большую гибкость.