Может ли абстрактный базовый класс считать тип совместимым при проверке экземпляра, если тип не наследуется от него, но имеет нужные методы?
Да, если абстрактный базовый класс переопределяет subclasshook. Этот метод позволяет определить структурную совместимость типа по наличию нужных атрибутов или методов, поэтому проверка экземпляра может пройти без наследования от ABC.
Это не происходит автоматически для любого абстрактного класса: сам класс должен явно реализовать такую логику. Признанный совместимым тип не получает методы ABC и не изменяет свою иерархию классов.
Абстрактные базовые классы появились в Python для унификации проверки возможностей объектов и устранения необходимости требовать явного наследования от общего базового класса. Это особенно важно для встроенных и сторонних типов, которые уже поддерживают нужную семантику, но не могут наследоваться от вашего класса.
Механизм связан с модулем abc и метаклассом ABCMeta. Он сочетает обычное наследование, виртуальную регистрацию классов и ограниченную структурную проверку через subclasshook. Это не то же самое, что статические протоколы из typing.Protocol: здесь проверка выполняется механизмом ABC во время работы программы.
Допустим, библиотека работает с объектами, у которых есть метод закрытия ресурса. Требовать наследование от конкретного базового класса неудобно: сторонние классы уже существуют, а изменение их иерархии невозможно или нежелательно.
Если принять любой объект с атрибутом нужного имени без дополнительных правил, можно получить ложное соответствие: атрибут окажется не вызываемым, будет иметь неподходящую сигнатуру или случайно совпадёт по имени. Кроме того, структурное признание не добавляет объекту реализацию методов ABC и не делает его настоящим наследником.
subclasshook вызывается метаклассом ABC при проверках совместимости класса. Через него косвенно работает и проверка экземпляра: сначала Python определяет класс объекта, затем проверяет, считается ли этот класс подклассом ABC.
Метод может вернуть:
Минимальный пример:
Проверка через subclasshook не добавляет close в FileLike, не меняет его __mro__ и не предоставляет реализацию по умолчанию. Она лишь влияет на результат isinstance и issubclass через механизм ABC.
Важно отличать этот механизм от register. Регистрация виртуального подкласса явно сообщает ABC, что конкретный класс следует считать совместимым, но не проверяет наличие методов. subclasshook подходит для общего структурного правила, а регистрация — для контролируемого списка известных типов.
Проверка обычно должна быть консервативной. Наличие атрибута по имени не гарантирует корректную сигнатуру, поведение или соблюдение контракта. Поэтому сложные проверки в __subclasshook__ могут создавать неожиданные совпадения, а чрезмерно широкое правило — ошибочно принимать неподходящие классы.
Внутренний сервис принимает объекты с методом close, включая типы из сторонних библиотек. Команда рассмотрела три варианта.
close, но повышает риск ложного соответствия и не проверяет полноценное поведение метода.Для небольшого стабильного контракта выбрали __subclasshook__, проверяющий именно вызываемость метода, а фактические ошибки закрытия обрабатывающий отдельно. Для более сложного API использовали бы явный адаптер или регистрацию: структурной проверки имени метода недостаточно, чтобы гарантировать семантическую совместимость.
Нет. Это виртуальное признание совместимости для механизмов issubclass и isinstance. Класс не появляется в __mro__, не получает методы ABC и не наследует его атрибуты. Если нужен общий код или гарантированная реализация, одного __subclasshook__ недостаточно.
Python не трактует это как отказ. Значение NotImplemented означает, что данный метод не вынес решения. Затем могут быть проверены обычное наследование, зарегистрированные виртуальные подклассы и другие правила метакласса ABC. Поэтому NotImplemented обычно является правильным результатом для классов, к которым структурное правило не относится.
Нет. Обычно он может проверить наличие атрибута, его вызываемость или присутствие метода в цепочке классов. Он не гарантирует правильную сигнатуру, типы аргументов, побочные эффекты и соблюдение семантического контракта. Для сложного интерфейса нужны явные тесты, адаптер, документация или более строгая архитектура API.