当
T, *Ts, **P
直接用在类或函数名称后面的方括号中或与 type
关键字一起使用时,它们意味着什么?
class ChildClass[T, *Ts, **P]: ...
def foo[T, *Ts, **P](arg: T) -> Callable[P, tuple[T, *Ts]]:
type vat[T, *Ts, **P] = Callable[P, tuple[T, *Ts]]
请参阅有关函数参数和参数的 ** 和 * 的补充问题:
PEP 695 相关,带来了一些 语法 它将 typing 库中的一些功能引入到语言本身的语法中。
简而言之就是每一个0-2个开始的表达式T, *Ts, **P
一个类型参数,它们被用作类型参数列表的参数,可用于函数,类和
type
语句来创建一个
TypeAliasType
。
由 []
封装的类型参数列表可以跟在
type
语句之后的类、函数或类型变量的名称,并将创建一个generic。类型检查器可以推断属性和调用的类型,例如:
class ClassA[T]:
def method1(self) -> T:
...
def create[T, *Ts](arg: T, *args: *Ts) -> ClassA[tuple[T, *Ts]]:
...
a = create(1, 2, 3) # a inferred as Class[tuple[int, int, int]]
t = a.method1() # t inferred as tuple[int, int, int]
T
相当于
typing.TypeVar
,并被解释为 单一类型。
*Ts
,相当于typing.TypeVarTuple,并被解释为 types 的元组。
*
之前的星号前缀
TypeVarTuple
相当于使用typing.Unpack,这类似于仅针对类型的正常解包,例如
tuple[int, *tuple[int, str]]
相当于
tuple[int, int, str]
。使用
TypeVarTuple
可以使解包的类型可变。
**P
,表示ParamSpec,它又代表可调用的参数。
0-2 星号的三种不同变体区分这些类型。
函数和类的类型参数列表仅在其本地范围内定义列出的类型参数,类似于 typing.Self,它指的是 class
' 体内封装类的类型。
def overly_generic[
SimpleTypeVar,
TypeVarWithBound: int,
TypeVarWithConstraints: (str, bytes),
*SimpleTypeVarTuple,
**SimpleParamSpec,
](
a: SimpleTypeVar,
b: TypeVarWithBound,
c: Callable[SimpleParamSpec, TypeVarWithConstraints],
*d: SimpleTypeVarTuple,
): ...
此外,ParamSpec
的
args
和
kwargs
属性也可用于注释两个或仅两个以
*
和
**
为前缀的函数参数
def usage_with_kwargs[
SimpleTypeVar,
**SimpleParamSpec,
](
c: Callable[SimpleParamSpec, SimpleTypeVar],
*c_args: SimpleParamSpec.args,
**c_kwargs: SimpleParamSpec.kwargs
): ...
:
,这些地址解决了
typing.TypeVar
的其他一些参数,允许定义类型变量的上层 bound 类型或 约束 表明类型是类型 A 或 B 或 ...
TypeVarWithBound: int # equivalent to TypeVar("TypeVarWithBound", bound=int)
TypeVarWithConstraints: (str, bytes), # equivalent to TypeVar("TypeVarWithConstraints", str, bytes)
type
语句的类型参数列表也是如此,只是在 Python 版本中<3.12 we have to use
typing_extensions.TypeAliasType
或 typing.Generic
中,前者与 type
语句的功能等效。引用
PEP 695:
在 python 3.12 之前定义泛型类看起来像这样:
from typing import Generic, TypeVar
_T_co = TypeVar("_T_co", covariant=True, bound=str)
class ClassA(Generic[_T_co]):
def method1(self) -> _T_co:
...
使用新语法,它看起来像这样,省略了直接使用 TypeVar
的需要。
class ClassA[T: str]:
def method1(self) -> T:
...
type
声明,它是类似的:
from collections.abc import Callable
type call[T, **P, *Ts] = Callable[P, tuple[T, *Ts]]
相当于
from typing import ParamSpec, TypeVarTuple, TypeVar, Unpack #, TypeAliasType # if Python >= 3.12
from typing_extensions import TypeAliasType # backport
from collections.abc import Callable
T = TypeVar('T')
P = ParamSpec('P')
Ts = TypeVarTuple("Ts")
call = TypeAliasType("call", Callable[P, tuple[T, Unpack[Ts]]], type_params=(T, P, Ts))