Примеры с кодом к вебинару для Яндекс.Практикум. 23й спринт (2ой спринт по FastAPI)
T = TypeVar("T")
# T – произвольный тип без ограничений
T = TypeVar("T", int, float)
# T – тип связанный только с типами int и float
T = TypeVar("T", bound=Geom)
# T – тип класса Geom или его дочернего класса
это механизм для создания классов, которые могут работать с разными типами данных, сохраняя при этом строгую типизацию.
Старый синтаксис class Stack(Generic[T]): pass
для python=^3.12 class Stack[T]: pass
https://mypy.readthedocs.io/en/stable/generics.html
- Указывая
Generic[T]
в качестве одного из родителей класса, мы объявляем, что класс является дженериком - Внутри этого класса можно использовать
T
как тип данных (как видно в полеdata: T
) - При создании конкретного экземпляра класса,
T
заменяется на указанный тип
- Повторное использование кода
- Типовая безопасность
- Подсказки IDE
- Самодокументирование:
Без дженериков вам пришлось бы:
- Создать отдельный класс для каждого типа данных (например,
UserResponse
,ProductResponse
) или - Использовать динамическую типизацию (например,
data: Any
), что менее безопасно и информативно
это функция, которая передаётся в другую функцию в качестве аргумента и вызывается внутри этой функции для выполнения определённой задачи.
- Передача функции как параметра — вы передаёте функцию в качестве аргумента другой функции
- Отложенное выполнение — callback функция не выполняется сразу, а вызывается в определённый момент внутри основной функции
- Инверсия контроля — вы передаёте часть логики программы "наружу", давая возможность внешнему коду определять, что должно произойти
- Обработка асинхронных операций
- Обработка событий
- Делегирование части логики
- Создание гибких и переиспользуемых компонентов
Callback функции делают код более гибким и модульным, позволяя отделить общую логику от специфичной и легко изменять поведение программы без изменения основной структуры кода.
- Не могут быть инстанцированы напрямую
- Наследники должны реализовать все абстрактные методы
- Обеспечивают проверку соответствия интерфейсу в момент создания класса
ABCMeta - документация - https://docs.python.org/3/library/abc.html#abc.ABCMeta
проверяeт следует ли какой-то класс или объект заранее определенному интерфейсу.
- Реализуют структурную типизацию (утиная типизация, duck typing)
- Класс соответствует протоколу, если у него реализованы все необходимые методы
- Не требуют явного наследования
- Используются преимущественно для статической типизации и проверок типов
Паттерн програмирования - "интерфейс".
означает, что типы должны точно совпадать без какой-либо иерархии наследования.
- При инвариантности, если
A
является подтипомB
, то контейнер типаContainer[A]
не является ни подтипом, ни супертипомContainer[B]
. - Это наиболее строгое отношение между типами.
позволяет использовать более конкретный тип там, где ожидается более общий тип.
- Если
A
является подтипомB
, тоContainer[A]
является подтипомContainer[B]
. - Ковариантность обычно безопасна для контейнеров, которые предоставляют доступ только для чтения (immutable), так как это гарантирует, что мы не сможем вставить несовместимые типы в контейнер.
противоположность ковариантности. Она позволяет использовать более общий тип там, где ожидается более конкретный.
- Если
A
является подтипомB
, тоContainer[B]
является подтипомContainer[A]
(обратная иерархия). - Это обычно безопасно для контейнеров, которые только потребляют значения (например, только для записи).
С точки зрения логической безопасности типов, функция, ожидающая более конкретный тип (например, str
), может использоваться там, где требуется функция, принимающая более общий тип (например, object
). Это потому, что:
- Если функция
f
может обрабатывать любой объект (object
), - А вызывается она только со строками (
str
), - То функция
g
, которая специализируется только на строках, справится с этой задачей.
object > Animal > Dog (иерархия типов)
Для контравариантных компараторов иерархия меняется на противоположную:
Comparator[object] < Comparator[Animal] < Comparator[Dog] (иерархия компараторов)
Это означает, что Comparator[object]
может быть использован везде, где требуется Comparator[Dog]
, но не наоборот.
Такая типизация позволяет:
- Повторно использовать общие реализации — вместо создания отдельных компараторов для каждого типа можно написать один общий
- Обеспечить безопасность типов — статические анализаторы кода могут проверить правильность использования компараторов
- Улучшить читаемость кода — аннотация явно показывает, какой тип объектов должен уметь сравнивать компаратор
В современной разработке такой подход активно используется для создания гибких, расширяемых и типобезопасных API.
Вопросы???