PEP解读 第1期 Python3.12-PEP695-类型形参语法概述

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划线警告:

  1. a, b和返回值不是Number
  2. 虽然它们都是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
4 个赞

厉害的