为了更好地支持泛型(Generics), Python 3.12中为声明泛型中的类型参数提供了更加便利的语法形式。作为对比,让我们先看看Python 3.11中是如何声明泛型参数类型的。
# demo——3.11.py - demonstrate type variables in generics
# CopyLeft by "Bruce Jia" if it works; Otherwise I don't know who wrote it, :)
#
import sys
print(sys.version) # just showing which version we're using
from typing import TypeVar
from collections.abc import Sequence
T = TypeVar("T")
def min(l: Sequence[T]) -> T:
# ignore the logic ...
return l[0]
def max(l: Sequence[T]) -> T:
# ignore the logic ...
return l[1]
print(min([1,2]))
print(max([1,2]))
这上面的代码实现了两个泛型函数min和max,因为仅仅是为了展示语言特性没有真正实现逻辑。代码能够在Python 3.11和3.12中运行。注意类型T被定义为一个typing.TypeVar,这样Python的解释器看到min和max的函数签名时就不会发懵了。如果不先定义T,那么就会出现下面的错误。
这样每次都需要一条语句来定义类型T,也很繁琐。所以Python 3.12中进行了改进,可以写得更加简洁。
# demo_3_12.py - demonstrate type variables in generics
# CopyLeft by "Bruce Jia" if it works; Otherwise I don't know who wrote it, :)
#
import sys
print(sys.version) # just showing which version we're using
from collections.abc import Sequence
def min[T](l: Sequence[T]) -> T:
# ignore the logic ...
return l[0]
def max[T](l: Sequence[T]) -> T:
# ignore the logic ...
return l[1]
print(min([1,2]))
print(max([1,2]))
上面代码中在函数名min/max的后边声明类型T,放入方括号中就可以了。Python解释器做语法分析的时候就知道T是一个类型定义了。
有一点需要注意的是,如果将T定义为typing.TypeVar("T") 之后,T在当前的范围内是有明确定义的,所以后边的函数或者类使用T的时候都是没有问题的。但是如果是通过Python 3.12的语法声明了T, 那么这时候T的有效范围仅仅是声明它的函数或者类定义中有效。例如
def avg(l: Sequence[T]) -> T:
# ...
return l[2]
print(avg([1, 3, 2, 2]))
如果将这段代码放在demo_3.11.py的尾部,是可以运行的;但是放在demo_3.12.py末尾,Python会报错,因为T没有被定义。
【上面文字中有点地方会出现声明和定义两个词,这里的目的是想借用C/C++中的概念。对于Python中的类型,使用“显式定义”和“隐式定义”或许更好,因为根本不存在什么声明的概念。哪位同学有更好的方法将这个讲清楚,欢迎交流指导。】
跟类型参数定义紧密相关的还有一个新东西,就是可以使用type语句来定义一个类型。例如,
# demo_typealias.py
# CopyLeft by "Bruce Jia" if it works; Otherwise I don't know who wrote it, :)
#
type Point = tuple[float, float]
class Rectangle:
def __init__(self, min: Point, max: Point):
self._min = min
self._max = max
def area(self) -> float:
# ignore ...
return 0
def calc_distance(p1: Point, p2: Point) -> float:
# ignore the implementation ...
return 3.14159
类似于C++中的typedef,这里通过type语句定义了类型Point, 它实际上是一个包含两个float元素的元组。定义了Point之后,我们可以使用它定义其他的类或者函数。那么这里的Point的类型,到底是什么呢?我们可以问问解释器。
print(type(Point))
Python会告诉我们,它是 <class 'typing.TypeAliasType'>。嗯,对,它就是一个类型别名。
(限于时间,今天就短-更到这里了。后面继续加油~~,大家一起努力学习,用Python写出更加优雅的代码~)
各位小伙伴如果对于类型参数的语法感兴趣,也可以追本溯源看看PEP 695。PEP 695 – Type Parameter Syntax | peps.python.org