Как реализовать поведение интерфейсов в Python
Что проверяют
Понимаете ли вы, что в Python нет ключевого слова interface, и знаете ли три способа описать контракт: утиная типизация, абстрактные базовые классы и протоколы. Главный уточняющий вопрос: чем ABC отличается от Protocol и когда что брать.
Как отвечать
- утиная типизация: объекту достаточно иметь нужные методы, наследование не требуется. Так устроена вся стандартная библиотека:
len()работает с любым объектом, где есть__len__; abc.ABCи@abstractmethod: номинальный контракт. Класс обязан унаследоваться и переопределить абстрактные методы, иначе при попытке создать экземпляр будетTypeError. Проверка происходит именно при создании объекта, а не при объявлении класса. В ABC можно положить общую реализацию, а черезregister()объявить виртуальный подкласс без наследования;typing.Protocol(с Python 3.8, PEP 544): структурный контракт. Класс ничего не наследует, mypy или pyright сами сверяют, что методы и сигнатуры совпадают. С@runtime_checkableпротокол работает вisinstance(), но проверяет только наличие атрибутов, не сигнатуры.
Практическое правило: Protocol для зависимостей, которые вы принимаете (репозиторий, клиент API, логгер), особенно если реализации приходят из чужих библиотек. ABC там, где нужна общая логика в базовом классе и явная иерархия, как в системе плагинов. Стоит упомянуть готовые ABC из collections.abc: Iterable, Mapping, Sequence. Унаследовавшись от Mapping и реализовав три метода, вы бесплатно получаете get, keys, items и оператор in.
Где спотыкаются
Путают raise NotImplementedError в базовом классе с абстрактным методом. Такой класс спокойно создаётся, а ошибка всплывёт только при вызове метода, уже в проде.
Считают, что @runtime_checkable гарантирует правильные сигнатуры. isinstance() вернёт True для объекта с методом save любой формы, реальную проверку даёт только статический анализатор.
Тащат в Python джавовский стиль: интерфейс на каждый класс и глубокие иерархии. Интервьюеру интереснее услышать, что протокол можно описать рядом с тем кодом, который его использует, и что без тайпчекера в CI он остаётся просто документацией.