Отличие Protocol от абстрактных классов

class ReadableService(Protocol, Generic[T]):
    def get_by_id(self, id: int) -> T:
        pass

    def get_all(self) -> list[T]:
        pass

Как я понимаю Protocol "создает договор", какие методы должны быть определены. Но по сути ABC (абстрактный класс) делает то же самое.

В чем отличие этих реализаций?


Ответы (1 шт):

Автор решения: Andrey Tabakov

Protocol это по сути своей интерфейс или контракт вызова. Т.е. вы просто говорите, что класс, который был передан в качестве аргумента куда-то, имеет эти методы/сигнатуры. Вы показываете, что у класса есть такие методы и их можно вызывать с определенными аргументами, получая ответ. Это так называемая утиная типизация (duck typing).

class UserService:
    def get_by_id(self, id: int) -> User: ...
    def get_all(self) -> list[User]: ...

def foo(service: ReadableService[User]):  # можно передать UserService даже без явного наследования
    ...

При ABC - вы явно указываете, что класс является наследником (имеет родителя) с указанными методами/сигнатурами.

class ReadableService(ABC, Generic[T]):
    @abstractmethod
    def get_by_id(self, id: int) -> T: ...

class UserService(ReadableService[User]):  # Мы обязаны указать, кто является родителем
    ...

Основное отличие в том, что Protocol используется больше статическими анализаторами кода. Т.е. какой-нибудь mypy может проверить по коду, что вызов определённого метода возможен, так как сигнатура соответствует. Т.е. он никак не влияет на интерпретацию/исполнение. Если мы забудем реализовать метод протокола, но при этом метод этого же протокола не будет вызван, то с точки зрения интерпретатора никакой проблемы не будет.

А вот при ABC мы обязаны переопределить абстрактные методы класса, иначе интерпретатор не сможет создать объект класса.

→ Ссылка