Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Ранее я уже рассказывал о сужении типов с помощью isinstance()
, assert
и Literal
. В сегодняшней заметке мы рассмотрим TypeGuard
, новый специальный тип, который позволяет нам создавать кастомные функции сужения типов.
TypeGuard
был определен в PEP 647 и доступен в Python 3.10+ или в более старых версиях из typing-extensions. Гвидо ван Россум добавил поддержку в Mypy в версии 0.900, которая была опубликована накануне.
Напомним, что сужение типов использует определенные выражения для вывода о том, что в данном блоке переменная имеет более ограниченный тип, чем ее определение. Например, с помощью isinstance()
:
from __future__ import annotations
name: str | None
if isinstance(name, str):
# name must be 'str'
...
else:
# name must be None
...
Средства проверки типов, включая Mypy, поддерживают ограниченное количество выражений, таких как if isinstance(...)
. Но количество потенциально сужающих тип выражений бесконечно, особенно для параметризованных типов, таких как контейнеры. TypeGuard
позволяет нам написать любое выражение типа и сообщить нашему средству для проверки типов, что оно их сужает.
Сужающая тип функция — такая, которая принимает хотя бы один аргумент и возвращает bool
. Вместо того чтобы обозначить возвращаемый тип как bool
, мы используем TypeGuard[T]
, где True
означает, что первый аргумент имеет тип T
, а False
— нет. Возьмем этот пример, адаптированный из PEP:
from __future__ import annotations
from typing_extensions import TypeGuard
def is_str_list(value: list[object]) -> TypeGuard[list[str]]:
"""Are all list items strings?"""
return all(isinstance(x, str) for x in value)
x: list[object]
reveal_type(x)
if is_str_list(x):
reveal_type(x)
is_str_list()
возвращает True
, если в заданном списке содержатся только строки. Мы сообщаем Mypy, что он может сузить тип value
до list[str]
с помощью возвращаемого типа TypeGuard
.
Запустив Mypy на этом файле, мы видим следующий результат вызова reveal_type()
:
$ mypy --strict example.py
example.py:13: note: Revealed type is "builtins.list[builtins.object]"
example.py:15: note: Revealed type is "builtins.list[builtins.str]"
Второе примечание показывает, что Mypy знает, что x
должен быть списком строк в блоке if
. Это позволяет нам использовать элементы списка в качестве str
без каких-либо ошибок. Отлично!
TypeGuard
отличается гибкостью, поскольку позволяет нам писать произвольный код для сужения выражений. Правда, он заставляет нас переносить даже короткие выражения в отдельные функции, но это часто бывает полезно для читабельности кода.
Поскольку существует бесконечное множество возможных выражений, средства проверки типов не могут подтвердить, что выбранные нами выражения соответствуют защищённым типам. Поэтому функции TypeGuard
нужно писать с осторожностью и тщательно тестировать.
PEP 647 также показывает общие функции TypeGuard
с TypeVar
, но когда я попробовал примеры, то обнаружил, что Mypy 0.901 пока не поддерживает их. Для TypeGuard существует несколько открытых проблем, так что, похоже, Mypy может воспользоваться нашим вкладом в их решение!
И пусть ваши типы будут хорошо защищены.
Приглашаем всех желающих на открытое занятие по теме «Знакомство с веб разработкой на Flask». На этом занятии мы с вами познакомимся с основами веб-разработки на Flask, а также научимся создавать и рендерить шаблоны страниц. Попробуем создать Flask приложение, затем создать роуты и в конце обработать различные HTTP методы на Flask. Регистрация по ссылке.