Typing
Компонент стандартной библиотеки, используемый для [type-annotation] сложных объектов. Используется в [mypy]
Основные принципы
Aliaces
Псевдонимы служат для сокращения аннотирующих конструкций
Vector = list[float]
def scale(scalar: float, vector: Vector) -> Vector:
return [scalar * num for num in vector]
New Type
Специальная обертка, необходимая когда требуется новый тип, без затрат на создание класса
from typing import NewType
UserId = NewType('UserId', int)
def get_user(x: UserId): ...
get_user(UserId(123456)) # this is fine
get_user(123456) # that's an int, not a UserId
UserId(123456) + 123456 # fine, because UserId is a subclass of int
Callable
Аннотация вызываемых объектов
Callable[[Arg1Type, Arg2Type], ReturnType] или так Callable[..., ReturnType]
Вызываемые объекты, которые принимают другие вызываемые объекты в качестве аргументов, могут указывать, что их типы параметров зависят друг от друга, используя ParamSpec. Кроме того, если этот вызываемый объект добавляет или удаляет аргументы из других вызываемых объектов, может использоваться оператор Concatenate. Они принимают форму Callable[ParamSpecVariable, ReturnType] и Callable[Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable], ReturnType] соответственно.
Generics
Поскольку информация о типах объектов, хранящихся в контейнерах, не может быть статически выведена универсальным способом, абстрактные базовые классы были расширены для поддержки обозначения ожидаемых типов для элементов контейнеров.
from collections.abc import Mapping, Sequence
def notify_by_email(employees: Sequence[Employee],
overrides: Mapping[str, str]) -> None: ...
Пользователи могут определять собственные дженерики, наследуясь от Generic с помощью TypeVar
TypeVar
Переменные типа позволяют ссылаться на один и тот же тип более одного раза, не указывая точно, какой это тип.
У TypeVar есть еще несколько вариантов. Вы можете ограничить возможные значения, передав больше типов (например, TypeVar(name, int, str)), или вы можете указать граничное состояние, чтобы каждое значение переменной типа было подтипом этого типа (например, TypeVar(name, bound=int)). Кроме того, вы можете решить, является ли переменная типа ковариантной, контравариантной или ни той, ни другой при ее объявлении. По сути, это решает, когда подклассы или суперклассы могут использоваться вместо универсального типа.
Без TypeVar не было бы хорошего способа реализовать следующий пример
from typing import TypeVar, Generic, List, Tuple
# Two type variables, named T and R
T = TypeVar('T')
R = TypeVar('R')
# Put in a list of Ts and get out one T
def get_one(x: List[T]) -> T: ...
# Put in a T and an R, get back an R and a T
def swap(x: T, y: R) -> Tuple[R, T]:
return y, x
# A simple generic class that holds a value of type T
class ValueHolder(Generic[T]):
def __init__(self, value: T):
self.value = value
def get(self) -> T:
return self.value
x: ValueHolder[int] = ValueHolder(123)
y: ValueHolder[str] = ValueHolder('abc')
Типичный пример
T = TypeVar('T') # Can be anything
A = TypeVar('A', str, bytes) # Must be str or bytes
The Any type
Особым типом является Any. Средство проверки статического типа будет рассматривать каждый тип как совместимый с Any, а Any — как совместимый со всеми типами.
Duck typing
typing поддерживает утиную типизацию, что означает, что классы реализующие соответствующее поведение не обязаны наследоваться от всех классов, подразумеваемых в типизируемом.
Специальные типы и примитивы
AnyNoReturnспециальный тип, указывающий, что функция никогда не возвращает значение
from typing import NoReturn
def stop() -> NoReturn:
raise RuntimeError('no way')
TypeAliasСпециальная аннотация для явного объявления алиаса
from typing import TypeAlias
Factors: TypeAlias = list[int]
Tuple(Tuple[int, float, str],Tuple[()],Tuple[int, ...].)UnionэквивалентX | Y(последний вариант рекомендуем)
Нюансы реализации:
Union[Union[int, str], float] == Union[int, str, float]
Union[int] == int
Union[int, str, int] == Union[int, str] == int | str
Union[int, str] == Union[str, int]
OptionalэквивалентX | NoneилиUnion[X, None]CallableConcatenateType(Generic[CT_co])Переменная, аннотированная с помощью C, может принимать значение типа C.
a = 3 # Has type 'int'
b = int # Has type 'Type[int]'
c = type(a) # Also has type 'Type[int]'
Literalуказывает на один или несколько литералов
def validate_simple(data: Any) -> Literal[True]: # always returns True
...
MODE = Literal['r', 'rb', 'w', 'wb']
def open_helper(file: str, mode: MODE) -> str:
...
open_helper('/some/path', 'r') # Passes type check
open_helper('/other/path', 'typo') # Error in type checker
ClassVarРазметка переменных класса
class Starship:
stats: ClassVar[dict[str, int]] = {} # class variable
damage: int = 10 # instance variable
Finalуказывает на то. что переменная не может быть переопределена в сабклассах
MAX_SIZE: Final = 9000
MAX_SIZE += 1 # Error reported by type checker
class Connection:
TIMEOUT: Final[int] = 10
class FastConnector(Connection):
TIMEOUT = 1 # Error reported by type checker
Annotatedанотация с помощью метаданных, например так:Annotated[int, ValueRange(3, 10), ctype("char")]TypeGuardИспользуется для аннотации возвращаемого типа с помощью определяемой пользователем guard функции, когда тип нельзя установить корректно на этапе объявления.TypeGuardпринимает только один аргумент. Во время выполнения функции, помеченные таким образом, должны возвращать логическое значение. Добавлен в 3.10
def is_str_list(val: List[object]) -> TypeGuard[List[str]]:
'''Determines whether all objects in the list are strings'''
return all(isinstance(x, str) for x in val)
def func1(val: List[object]):
if is_str_list(val):
# Type of ``val`` is narrowed to ``List[str]``.
print(" ".join(val))
else:
# Type of ``val`` remains as ``List[object]``.
print("Not a list of strings!")
Дженерики
GenericTypeVarParamSpecParamSpecArgsParamSpecKwargsAnyStrэквивалентTypeVar('AnyStr', str, bytes)Protocol(Generic)аннотирует проткол классов (поддерживает утиную типизацию в т.ч.)
class Proto(Protocol):
def meth(self) -> int:
...
class C:
def meth(self) -> int:
return 0
def func(x: Proto) -> int:
return x.meth()
func(C()) # Passes static type check
runtime_checkableрантайм проктол
Другие спец.директивы
В данном разделе не конструкции аннотации, а специальные типы, использующие аннотацию.
NamedTupleподдерживающий аннотацию вариантnamedtupleNewTypeTypedDictтип, поддерживающий аннотацию для словаря
Коллекция дженериков
Основные типы: List, Dict, FrozenSet, Set
Типы, поддерживающие модуль collections: DefaultDict, OrderedDict, ChainMap, Counter, Deque (смотри [deque], [chainmap], [counter], [ordereddict], [defaultdict])
Другие типы (в основном депрекейтед)
ABC
- связанные с
collections.abc - асинхронные объекты
- контекстные менеджеры
Протоколы
Функции и декораторы
- cast(typ, val) приводит переменную к типу, без изменеения значения
- @overload Декоратор @overload позволяет описывать функции и методы, поддерживающие различные комбинации типов аргументов. За серией декорированных @overload определений должно следовать ровно одно недекорированное @overload определение
@overload
def process(response: None) -> None:
...
@overload
def process(response: int) -> tuple[int, str]:
...
@overload
def process(response: bytes) -> str:
...
def process(response):
<actual implementation>
- @final запрет переопределения
- @no_type_check указание на то, что анотации не определяют тип
- @no_type_check_decorator декоратор декоратора
- @type_check_only определяет, что функция или класс не доступны в рантайм
Хелперы интроспекции
TYPE_CHECKING
True на првоерке и False в рантайм
if TYPE_CHECKING:
import expensive_mod
def fun(arg: 'expensive_mod.SomeType') -> None:
local_var: expensive_mod.AnotherType = other_fun()
Смотри еще: