Получить доступ

Чем абстрактный базовый класс (ABC) отличается от Protocol?

MiddleООПPython >=3.8
Ещё спрашивают так:
  • Что такое утиная типизация и как её описать в аннотациях?
  • Номинальная и структурная типизация в Python
Коротко

ABC — номинальная типизация: класс должен явно наследовать абстрактный класс, а нереализованные @abstractmethod не дадут создать экземпляр. Protocol (3.8) — структурная: подходит любой класс с нужными методами без наследования, проверку делает mypy или pyright — это утиная типизация в аннотациях.

  • ABC проверяется при выполнении, Protocol — статически (runtime_checkable — только наличие методов)
  • ABC может нести общую реализацию
  • Protocol удобен для чужих классов, которые нельзя изменить

А ещё collections.abc (Iterable, Mapping) — готовые ABC с __subclasshook__

Так подсказку видит пользователь sotto на живом собеседовании — за пару секунд, пока вы думаете вслух.

Подробно

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"...")

Что хотят услышать

  • Номинальная против структурной типизации.
  • Где происходит проверка.
  • Когда что выбрать.

Связанные вопросы

Закрытая бета

Подсказки на собеседовании — в реальном времени

sotto слышит вопрос интервьюера и показывает короткий ответ и план, как в блоке «Коротко». Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем