Вопрос с собеседования · Python

Чем абстрактный базовый класс (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__

Подробно

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

Разбор темы

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

Партнёр платформы

Разбор собеседования с sotto

После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.

Разбор собеседования