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
поддерживает утиную типизацию, что означает, что классы реализующие соответствующее поведение не обязаны наследоваться от всех классов, подразумеваемых в типизируемом.
Специальные типы и примитивы
Any
NoReturn
специальный тип, указывающий, что функция никогда не возвращает значение
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]
Callable
Concatenate
Type(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!")
Дженерики
Generic
TypeVar
ParamSpec
ParamSpecArgs
ParamSpecKwargs
AnyStr
эквивалент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
поддерживающий аннотацию вариантnamedtuple
NewType
TypedDict
тип, поддерживающий аннотацию для словаря
Коллекция дженериков
Основные типы: 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()
Смотри еще: