PEP695:https://peps.python.org/pep-0695/
前置/PEP484:https://peps.python.org/pep-0484/
此文主要从面上介绍 PEP695 的核心思想和关键变化,不涉及细节内容。
是什么
PEP 695 – Type Parameter Syntax,即 类型形参语法 ,提供了一种新的方式来确定泛型函数或者类。
用例:
from numbers import Number
def max[T: Number](a: T, b: T) -> T:
return a if a > b else b
该段代码声明了一个动态类型函数max(),其定义了一个涵盖Number类型的泛型T,接收两个T类型的参数,返回与两个参数类型一致的返回值。
Q:Python不是动态类型语言吗?为什么需要显式声明动态类型函数/类?
A:以C++为例,静态类型语言的编译期会对类型进行检查,如果类型不匹配,编译器会报错。因此C++中的
template <typename T>
等声明是严格的、强制的;而Python是动态类型语言,其声明只是一种“类型注释”,以供检查(点名批评vscode,表扬pycharm),只在开发期起到提示作用,不影响编译期和运行期。
为什么
既然是一种“类型注释”,那么它就不应该在编译期和运行期产生效应,我们只关注其担当注释职能的效果。
在Python3.11及以前,想要给一个泛型类或者泛型函数做类型注释,我们是这么做的:
# PEP484
# Python3.5+
from typing import TypeVar
from numbers import Number
T = TypeVar('T', bound=Number)
def max(a: T, b: T) -> T:
return a if a > b else b
而在Python3.12以后,我们可以:
from numbers import Number
def max[T: Number](a: T, b: T) -> T:
return a if a > b else b
该函数确定泛型T,泛型T支持的类型包含Number;确定函数接收两个参数a, b,类型为T,返回一个参数,类型也为T。其中a, b及返回值类型完全相同。
以下情况是不符合类型注释的,会被IDE划线警告:
a
,b
和返回值不是Number- 虽然它们都是Number,但是类型不一致,有的是int,有的是float…
怎么做
如何写好一个泛型函数/类的类型注释?
声明
对于函数:
def func[T: str]() -> T
对于类:
class ClassA[T: str]
多种类型变量
原教旨,使用元组声明:
def func[T: (Number, tuple)]()
元组必须包含至少两个元素,否则会报错。
def func[T: ()]() # Type checker error: two or more types required
def func[T: (str,)]() # Type checker error: two or more types required
def func[T: str]() # 正确的写法
元组不能在外部定义,不可以是
a = (Number, tuple)
def func[T: a]() # Type checker error: literal tuple expression required
泛型声明内不能再套泛型
def func[T: (list[S], str)]() #Type checker error: generic type
泛型声明不可以是该类型的实例
def func[T: (3, tuple)]() # Type checker error: invalid expression form
但是请注意,"ForwardReference"
是一个特殊的字符串,它用来指代未定义的类。
def func[T: ("ForwardReference", tuple)]() # OK