Чем абстрактный базовый класс (ABC) отличается от Protocol?
- Что такое утиная типизация и как её описать в аннотациях?
- Номинальная и структурная типизация в Python
ABC — номинальная типизация: класс должен явно наследовать абстрактный класс, а нереализованные @abstractmethod не дадут создать экземпляр. Protocol (3.8) — структурная: подходит любой класс с нужными методами без наследования, проверку делает mypy или pyright — это утиная типизация в аннотациях.
- ABC проверяется при выполнении, Protocol — статически (runtime_checkable — только наличие методов)
- ABC может нести общую реализацию
- Protocol удобен для чужих классов, которые нельзя изменить
А ещё collections.abc (Iterable, Mapping) — готовые ABC с __subclasshook__
Подробно
ABC — номинальная типизация. Тип определяется явным родством: class S3Storage(Storage). Если подкласс не реализовал все @abstractmethod, S3Storage() бросит TypeError. ABC может содержать общий код и шаблонные методы.
Protocol — структурная типизация. Тип определяется набором методов. Функция dump(out: Writable) примет файл, io.BytesIO, сокет-обёртку — всё, у чего есть write(bytes) -> int, без наследования. Проверку делает статический анализатор. @runtime_checkable позволяет isinstance, но проверяет только наличие методов, не сигнатуры.
Когда что:
- своя иерархия с общей логикой и проверкой при создании — ABC;
- описать требования к аргументу, который могут удовлетворить чужие классы, — Protocol;
- тестовые двойники проще писать под Protocol: наследование не нужно.
collections.abc — готовые ABC (Iterable, Sequence, Mapping), часть из них распознаёт классы по методам через __subclasshook__: класс с __iter__ — уже Iterable для isinstance.
Пример
from abc import ABC, abstractmethod
from typing import Protocol
class Storage(ABC):
@abstractmethod
def save(self, data: bytes) -> None: ...
class Writable(Protocol):
def write(self, data: bytes) -> int: ...
def dump(out: Writable) -> None: # подойдёт и файл, и BytesIO, и свой класс
out.write(b"...")Что хотят услышать
- Номинальная против структурной типизации.
- Где происходит проверка.
- Когда что выбрать.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✗Protocol требует явного наследования, а ABC — нет: ABC сверяет методы класса структурно, при каждом их вызове
- ✗Protocol полностью проверяется интерпретатором при каждом вызове метода
- ✗ABC и Protocol — синонимы, отличаются только модулем
- ✓ABC — номинальная типизация: нужно наследование, проверка при создании; Protocol — структурная, проверяет mypy
Разбор темы
- ООП в Python — классы, свойства, наследование и dataclass
self и __init__, магические методы, property, staticmethod и classmethod, MRO и super, dataclass против namedtuple и Pydantic, ABC и Protocol, метаклассы.
- Аннотации типов, mypy и Pydantic
Что дают аннотации и кто их проверяет, как аннотировать коллекции, Optional и объединения, обобщённые типы, mypy в проекте, проверка данных Pydantic.
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.