В Python 3.11 добавлена аннотация typing.TypeVarTuple
, которая представляет собой тип переменной кортеж. Специализированная форма переменной типа, которая позволяет использовать вариативные обобщения.
Другими словами, аннотация typing.TypeVarTuple
- это переменная типа с переменным числом аргументов, позволяющая использовать обобщения с переменным числом переменных.
В Python 3.12 кортежи переменных типа могут быть объявлены в списках параметров типа, используя одну звездочку *
перед именем:
def move_first_element_to_last[T, *Ts](tup: tuple[T, *Ts]) -> tuple[*Ts, T]: return (*tup[1:], tup[0])
Изменено в версии 3.12: кортежи переменных типа теперь можно объявлять с использованием синтаксиса параметров типа.
Или явным вызовом конструктора typing.TypeVarTuple
:
from typing import TypeVar, TypeVarTuple T = TypeVar('T') Ts = TypeVarTuple('Ts') def move_first_element_to_last(tup: tuple[T, *Ts]) -> tuple[*Ts, T]: return (*tup[1:], tup[0])
Переменная обычного типа typing.TypeVar
позволяет параметризовать один тип. Кортеж переменных типа typing.TypeVarTuple
, напротив, допускает параметризацию с произвольным количеством типов, действуя как произвольное количество переменных типа, заключенных в кортеж. Например:
# `T` привязан к `int`, `Ts` привязан к `()` # Возвращаемое значение `(1,)` имеет тип `tuple[int]` move_first_element_to_last(tup=(1,)) # `T` привязан к `int`, `Ts` привязан к `(str,)` # Возвращаемое значение `('spam', 1)` имеет тип `tuple[str, int]` move_first_element_to_last(tup=(1, 'spam')) # `T` привязан к `int`, `Ts` привязан к `(str, float)` # Возвращаемое значение `('spam', 3.0, 1)` имеет тип `tuple[str, float, int]` move_first_element_to_last(tup=(1, 'spam', 3.0)) # Ошибка при вводе (и ошибка во время выполнения) # потому что `tuple[()]` несовместим с `tuple[T, *Ts]` # (требуется хотя бы один элемент) move_first_element_to_last(tup=())
Обратите внимание на использование оператора распаковки
*
вtuple[T, *Ts]
. Концептуально можно думать оTs
как о кортеже переменного типа(T1, T2,...)
.tuple[T, *Ts]
станет тогдаtuple[T, *(T1, T2, ...)]
, что эквивалентноtuple[T, T1, T2, ...]
. (Обратите внимание, что в более старых версиях Python это может быть написано с использованиемtyping.Unpack
вместоUnpack[Ts]
.)
Кортежи переменных типа всегда должны быть распакованы звездочкой (*
). Это помогает отличить кортежи переменных типа от переменных обычного типа:
x: Ts # Недействительно x: tuple[Ts] # Недействительно x: tuple[*Ts] # Правильный способ
Кортежи переменных типа могут использоваться в тех же контекстах, что и переменные обычного типа. Например, в определениях классов, аргументах и возвращаемых типах:
Shape = TypeVarTuple('Shape') class Array(Generic[*Shape]): def __getitem__(self, key: tuple[*Shape]) -> float: ... def __abs__(self) -> Array[*Shape]: ... def get_shape(self) -> tuple[*Shape]: ...
Кортежи переменных типа можно успешно комбинировать с переменными обычного типа:
DType = TypeVar('DType') # Это хорошо class Array(Generic[DType, *Shape]): pass # Это тоже неплохо class Array2(Generic[*Shape, DType]): pass # хорошо float_array_1d: Array[float, Height] = Array() # это тоже хорошо int_array_2d: Array[int, Height, Width] = Array()
Обратите внимание, что не более одного кортежа переменных типа может присутствовать в одном списке аргументов типа или параметров типа:
# Недействительно x: tuple[*Ts, *Ts] # Недействительно class Array(Generic[*Shape, *Shape]): pass
Наконец, в качестве аннотации типа *args
можно использовать распакованный кортеж переменных типа:
def call_soon( callback: Callable[[*Ts], None], *args: *Ts ) -> None: ... callback(*args)
В отличие от аннотаций, например, *args: int
, означающих, что все аргументы имеют тип int
, аннотация типа *args: *Ts
позволяет ссылаться на типы отдельных аргументов в *args
. Это позволяет гарантировать, что типы *args
, переданные в call_soon()
, соответствуют типам (позиционных) аргументов функции callback()
.
TypeVarTuple
имеет атрибут:TypeVarTuple.__name__
:Атрибут TypeVarTuple.__name__
представляет собой имя кортежа переменной типа.
Новое в Python 3.11.