Примечание: этот вопрос относится к Python 3.12+.
Предположим, у меня есть:
from typing import Any, TypeVar
import numpy as np
T = TypeVar("T")
U = TypeVar("U")
ListLike = T | list[T] | tuple[T, ...] | np.ndarray[Any, U]
ListLikeStr = ListLike[str, np.object_]
# ListLikeStr should be: str | list[str] | tuple[str, ...] | np.ndarray[Any, np.object_]
Это работает, но это повезло. Вместо этого я мог бы написать: ListLike[np.object_, str]
, и тогда я бы получил ListLikeStr
быть np.object_ | list[np.object_] | tuple[np.object_, ...] | np.ndarray[Any, str]
, а это не то, чего мне хотелось бы.
В идеале я мог бы сделать что-то вроде: ListLike[T=str, U=np.object_]
, но это не работает. Так что же определяет порядок, когда я создаю экземпляры переменных типа в ListLike
? Откуда ListLike
«знает», что T
соответствует str
, а U
— np.object_
, когда я пишу ListLike[str, np.object_]
?
В «традиционном» псевдониме типа первым идет ссылка на тот, на который ссылаются первым.
PEP 695type
были созданы, чтобы исправить это:
(playground link: Pyright)
type A[U: str, T] = T | list[T] | tuple[T, U]
a: A[int, str] # error: "int" is not a subtype of "str"
b: A[str, int] # fine
reveal_type(b) # str | list[str] | tuple[int, str]
Mypy еще не добавила его поддержку, но вы можете импортировать TypeAliasType
из typing_extensions
для достижения того же результата (все версии Python):
(playground link: Mypy, Pyright)
from typing import TypeVar
from typing_extensions import TypeAliasType
T = TypeVar('T')
U = TypeVar('U', bound = str)
A = TypeAliasType('A', T | list[T] | tuple[T, U], type_params = (U, T))
a: A[int, str] # error: "int" is not a subtype of "str"
b: A[str, int] # fine
reveal_type(b) # int | list[int] | tuple[int, str]