Есть ли в Python интерфейсы, как в Java
Что проверяют
Понимаете ли вы разницу между номинальной типизацией Java и утиной типизацией Python и знаете ли инструменты, которые её закрывают. Следом обычно спрашивают, чем ABC отличается от Protocol и когда что брать.
Как отвечать
Ключевого слова interface в Python нет, задачу интерфейса решают три механизма:
- утиная типизация: функции нужен объект с методом
read(), а не наследник конкретного класса. Это базовый стиль языка; - абстрактные классы
abc: наследникABCс методами под@abstractmethod. Подкласс, который реализовал не всё, не создаётся:TypeErrorвозникает при вызове конструктора, а не при объявлении класса. В отличие от интерфейса Java, в ABC можно держать общий код и состояние; typing.Protocol(Python 3.8, PEP 544): структурная типизация. Класс ничего не наследует, ему достаточно иметь нужные методы с подходящими сигнатурами, а соответствие проверяет mypy или pyright.
Как выбирать: ABC, когда нужен общий код и явная иерархия, например базовый класс плагина. Protocol, когда вы описываете, что функция ждёт от аргумента, и не хотите навязывать наследование, особенно чужим классам, которые вы не контролируете.
Пригодится знание стандартной библиотеки: часть классов из collections.abc (Iterable, Sized, Hashable) проходит isinstance без наследования, потому что у них есть __subclasshook__. Для своих ABC есть register(): он объявляет класс виртуальным подклассом без проверки методов.
Где спотыкаются
Считают, что Protocol проверяется при запуске. Без статического анализатора ошибки не будет, а @runtime_checkable для isinstance смотрит только на наличие атрибутов, сигнатуры не сравнивает.
Ставят @abstractmethod в класс, который не наследует ABC и не использует ABCMeta. Тогда декоратор ничего не запрещает, объект спокойно создаётся.
Переносят привычки из Java и заводят интерфейс на каждый класс с единственной реализацией. В Python это лишний слой, протокол на границе модуля обычно полезнее.