Отличие 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 шт):
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 мы обязаны переопределить абстрактные методы класса, иначе интерпретатор не сможет создать объект класса.