В чем разница между интерфейсами на основе ABC и Protocol в Python?

Ответ

Ключевое различие заключается в подходе к определению соответствия интерфейсу: ABC использует номинальную типизацию (явное наследование), а Protocolструктурную типизацию (утиную типизацию).

1. Интерфейс на основе abc.ABC (номинальная типизация)

Класс должен явно наследоваться от абстрактного базового класса (ABC) и реализовывать все его абстрактные методы, помеченные декоратором @abstractmethod. Соответствие проверяется во время выполнения (runtime).

Пример:

from abc import ABC, abstractmethod

class Serializable(ABC):
    @abstractmethod
    def serialize(self) -> bytes:
        pass

# Класс User ДОЛЖЕН наследоваться от Serializable
class User(Serializable):
    def serialize(self) -> bytes:
        return b'user_data'

# Этот код вызовет TypeError, т.к. метод не реализован
# class Product(Serializable):
#     pass
# p = Product()

2. Интерфейс на основе typing.Protocol (структурная типизация)

Классу не нужно наследоваться от протокола. Он считается совместимым, если имеет все методы и атрибуты, определённые в протоколе, с правильными сигнатурами. Проверка в основном выполняется статическими анализаторами кода (Mypy, Pyright).

Пример:

from typing import Protocol

class Serializable(Protocol):
    def serialize(self) -> bytes:
        ...

# Класс User НЕ наследуется от Serializable, но соответствует протоколу
class User:
    def serialize(self) -> bytes:
        return b'user_data'

# Статический анализатор проверит, что obj имеет метод .serialize()
def save(obj: Serializable):
    data = obj.serialize()
    print(f"Saving {len(data)} bytes")

save(User()) # Ошибок нет

Итог

Критерий abc.ABC typing.Protocol
Типизация Номинальная Структурная
Наследование Обязательно Не требуется
Проверка Runtime Static (Mypy)
Гибкость Низкая Высокая (можно "адаптировать" сторонние классы)

Ответ 18+ 🔞

А, слушай, вот тут тема интересная, блядь! Про то, как в Питоне интерфейсы делать. Так вот, есть два главных подхода, и разница между ними — это просто пиздец, нахуй, принципиальная. Один — это как официальная бумажка с печатью, а другой — как «ну ты ж понимаешь, ты же можешь».

Первый — это abc.ABC. Это, сука, номинальная типизация. То есть, чтобы считаться своим, ты должен явно нахуяриться от этого самого ABC и все его абстрактные методы реализовать. Иначе — пиздец, тебя на этапе создания объекта размажут по стенке с TypeError. Это как прийти на завод и сказать: «Я токарь». А тебе: «Ну покажи диплом, блядь!». Нет диплома — иди нахуй.

from abc import ABC, abstractmethod

class Serializable(ABC):
    @abstractmethod
    def serialize(self) -> bytes:
        pass

# Класс User ДОЛЖЕН наследоваться от Serializable
class User(Serializable):
    def serialize(self) -> bytes:
        return b'user_data'

# А вот этот уёбок не реализовал метод — получи пизды сразу!
# class Product(Serializable):
#     pass
# p = Product()  # Тут тебе и выедет: TypeError, мудак!

Второй — это typing.Protocol. Вот это, ёпта, уже структурная типизация, или, как её ещё обзывают, утиная. Суть проста: если что-то ходит как утка и крякает как утка, то похуй, что у него в паспорте написано «бобёр». Главное — методы есть. Наследоваться не надо, главное — структура совпадает. Проверяют это в основном всякие статические анализаторы вроде Mypy, они умные, они видят.

from typing import Protocol

class Serializable(Protocol):
    def serialize(self) -> bytes:
        ...

# Смотри, какая хуйня! Класс User НЕ наследуется от протокола!
class User:
    def serialize(self) -> bytes:
        return b'user_data'

# Но функция-то ждёт объект, который умеет .serialize()
def save(obj: Serializable):
    data = obj.serialize()
    print(f"Saving {len(data)} bytes")

save(User())  # И тут всё заебись! Mypy не орёт, runtime не падает. Магия, блядь!

Короче, итоговая таблица, чтобы в голове не ебалось:

Критерий abc.ABC typing.Protocol
Типизация Номинальная (диплом нужен) Структурная (главное — уметь)
Наследование Обязательно, блядь! Похуй, можно без него
Кто проверяет Runtime (интерпретатор, строгий, как тёща) Статический анализатор (Mypy, умный дядька)
Гибкость Низкая, жёсткая Высокая, овердохуищная (можно даже со сторонними библиотеками работать)

Так что выбирай, что тебе ближе: официальная бумажка с печатью или «и так сойдёт, главное — работает».