Python — это язык с динамической типизацией. Однако в нем встроены утилиты вроде type hints для более строгой проверки типов. Неуклонное следование таким подсказкам мы вынуждены загромождать свой код. Но не каждый программист или Data Scientis знает, что Python поддерживает еще и утиную типизацию (duck typing) через протокол. Об этом мы расскажем сегодня.
Что такое утиная типизация (duck typing)
Утиная типизация (duck typing) — это направление в программировании, где в расчет в первую очередь идет на поведение и свойства объекта, а не на его тип.
NLP с Python
Код курса
PNLP
Ближайшая дата курса
24 февраля, 2025
Продолжительность
40 ак.часов
Стоимость обучения
90 000 руб.
На практике это означает, что вы при написании функции должны волноваться только о поведении и/или атрибутах ее входных параметров, а не о их конкретных типах.
Если у вас имеется функция с параметрами заданных типов, то вы можете сделать их еще более гибкими благодаря утиной типизации.
Протоколы для поддержки duck typing
Проанализируем следующий код с функцией на Python:
def calculate_windowed_avg( measurements: Union[List[TemperatureMeasurement], List[HumidityMeasurement]], window_size: timedelta, field_name: str ) -> Dict[datetime, float]: window_upper_bound = measurements[0].timestamp + window_size current_window = [] window_averages = OrderedDict() for m in measurements: # various calculations happen here # based on the timestamp of each measurement ... return window_averages
Цель этой функции вычислить среднее для заданного поля field_name
. На момент написания функция используется для классов TemperatureMeasurement
и HumidityMeasurement
, но очень вероятно, что захочется применять ее и для других типов измерений в будущем.
Если присмотреться ближе, то мы увидим, что объект обязательно должен иметь свое поле timestamp
. Поэтому вместо указания этих длинных типов мы бы хотели, чтобы анализатор типов проверял наличие поля timestamp
, а не тип.
Класс Protocol
из модуля typing
поможет сделать это. Так же как и в утиной типизации протокол лишь указывает на ожидаемое поведение или атрибуты без всякой заботы о типе.
Поглядите, как теперь более прилично и читаемо выглядит сигнатура с использование протокола:
from typing import Protocol, List, Dict from datetime import datetime class MeasurementLike(Protocol): timestamp: datetime def calculate_windowed_avg( measurements: List[MeasurementLike], window_size: timedelta, field_name: str ) -> Dict[datetime, float]: window_upper_bound = measurements[0].timestamp + window_size
Теперь анализатор типов не знает точно, какой объект какого типа должен передаваться первым параметром, но он знает, что он должен иметь поле timestamp
, потому что мы указали это в протоколе MeasurementLike
.
В некотором роде протокол ведет себя как интерфейс в Java или TypeScript. Мы не вверяем желаемое поведение или атрибуты в тип, а в параметр функции. Это и есть демонстрация утиной типизации.
Протоколы и дженерики
Помимо протокола, есть класс TypeVar
, который предназначен для еще более обобщенных функций.
Его можно использовать, когда вас не волнует тип параметра функции, поскольку он запротоколирован, но тип возвращаемого значения совпадает с типом входного параметра.
Например, функция для перевода измерений с использование TypeVar
может выглядеть так:
from typing import Protocol, TypeVar from datetime import datetime class MeasurementLike(Protocol): timestamp: datetime M = TypeVar('M', bound=MeasurementLike) def measurement_as_timezone(measurement: M, tz: tzinfo) -> M: measurement.timestamp = measurement.timestamp.astimezone(tz) return measurement
Как видим, Python предоставляет большие возможности для разных видов типизации.
Еще больше о фишках написания читаемого кода вы узнаете на наших образовательных курсах в лицензированном учебном центре обучения и повышения квалификации руководителей и ИТ-специалистов (менеджеров, архитекторов, инженеров, администраторов, Data Scientist’ов и аналитиков Big Data) в Москве:
- DPREP: Подготовка данных для Data Mining на Python
- Нейронные сети на Python
- NLP с Python
- Computer vision на Python