Введение в протоколы в ООП в Python
Как вы уже знаете, абстрактные классы
задают набор методов для своих потомков
и требуют явного наследования. В Python
есть другой способ описать такой набор:
протокол из модуля typing.
Протокол описывает, какие методы должен иметь объект. Класс подходит под протокол, если у него есть эти методы, даже когда он не наследует протокол в объявлении.
Зачем это нужно: при программировании совершать меньше ошибок - описав нужные методы в протоколе, мы можем быть уверены, что передаем в функцию объект с нужным поведением.
Когда это поможет: пусть мы напишем функцию, которая работает с любым объектом, у которого есть метод имени. Через месяц коллега добавит новый класс и может забыть метод - протокол и проверка типов подскажут ошибку еще до запуска.
Протокол объявляют как класс, наследующий
Protocol. Методы внутри только
описывают сигнатуру, без реализации.
Объект протокола не создают: он нужен
как описание контракта.
Давайте сделаем протокол HasName
с методом name:
from typing import Protocol
class HasName(Protocol):
def name(self) -> str:
...
Класс User не наследует HasName,
но содержит тот же метод - значит, подходит
под протокол:
class User:
def __init__(self, name):
self.__name = name
def name(self):
return self.__name
Функция может принимать аргумент с типом
HasName, а мы передадим User:
def show_name(obj: HasName):
print(obj.name())
user = User('john')
show_name(user) # выведет 'john'
Так протокол заменяет идею интерфейса из других языков: важен набор методов, а не строка наследования в классе.