Ранее мы затронули типизацию в фикстурах (косвенно), поэтому сегодня поговорим про:
Protocol vs ABC: Утиная типизация на стероидах (Static Duck Typing)
В классическом ООП (Java, C#) и при использовании
Это создает жесткую связность (coupling): ваша реализация должна знать об интерфейсе и импортировать его.
С приходом
В чем суть?
Если класс имеет метод
Сравним код:
❌ Старый путь (ABC):
✅ Новый путь (Protocol):
Киллер-фича: Retroactive Abstraction (Ретроактивная абстракция)
Представьте, что вы используете стороннюю библиотеку (например,
С помощью
Нюансы для Middle+:
1. Runtime: По умолчанию
2. Свойства: В протоколе можно описывать не только методы, но и поля через
Используйте Протоколы, чтобы развязать зависимости между модулями. Это основа принципа Dependency Inversion в Python.
#python #typing #mypy #architecture #clean_code
📲 Мы в MAX
👉@BookPython
Protocol vs ABC: Утиная типизация на стероидах (Static Duck Typing)
В классическом ООП (Java, C#) и при использовании
abc.ABC в Python мы привыкли к Nominal Subtyping (Именная подтипизация). Чтобы объект считался Bird, он должен явно наследоваться от Bird.Это создает жесткую связность (coupling): ваша реализация должна знать об интерфейсе и импортировать его.
С приходом
typing.Protocol (Python 3.8+) мы получили Structural Subtyping (Структурная подтипизация).В чем суть?
Если класс имеет метод
quack(), то это Утка. Неважно, от чего он наследуется. Это и есть та самая «утиная типизация», но теперь поддерживаемая статическим анализатором (mypy, pyright, IDE).Сравним код:
❌ Старый путь (ABC):
from abc import ABC, abstractmethod
# 1. Жестко определяем интерфейс
class SenderABC(ABC):
@abstractmethod
def send(self, msg: str) -> None: pass
# 2. Обязаны наследоваться!
class EmailService(SenderABC):
def send(self, msg: str) -> None:
print(f"Email: {msg}")
def alert(sender: SenderABC):
sender.send("Alert!")
✅ Новый путь (Protocol):
from typing import Protocol
# 1. Описываем, "что мы ждем от объекта"
class SenderProto(Protocol):
def send(self, msg: str) -> None: ...
# 2. Реализация НИЧЕГО не знает про Protocol
# Никаких импортов и наследования!
class SmsService:
def send(self, msg: str) -> None:
print(f"SMS: {msg}")
# Mypy счастлив: SmsService имеет нужную структуру (метод send)
def alert(sender: SenderProto):
sender.send("Alert!")
alert(SmsService())
Киллер-фича: Retroactive Abstraction (Ретроактивная абстракция)
Представьте, что вы используете стороннюю библиотеку (например,
boto3 или клиент Redis). Вы не можете заставить их классы наследоваться от ваших ABC.С помощью
Protocol вы можете создать интерфейс для уже существующего чужого кода, не меняя его, и типизировать свои функции.Нюансы для Middle+:
1. Runtime: По умолчанию
isinstance(obj, MyProtocol) выбросит ошибку. Протоколы - это compile-time фича. Если нужна проверка в рантайме, декорируйте протокол @runtime_checkable.2. Свойства: В протоколе можно описывать не только методы, но и поля через
@property или просто аннотации типов.Используйте Протоколы, чтобы развязать зависимости между модулями. Это основа принципа Dependency Inversion в Python.
#python #typing #mypy #architecture #clean_code
📲 Мы в MAX
👉@BookPython
👍6