【Python】【编程思维】Python中的泛型

Union

作用:限制数据类型的范围

1
2
3
4
5
6
7
8
9
from typing import Union

def give_me_the_bigger_one(num0: Union[int, float], num1: Union[int, float]) -> Union[int, float]:
if type(num0) != type(num1):
raise ValueError(f"The two number {num0} and {num1} must have the same type!")
if num0 > num1:
return num0
else:
return num1

a: Union[int, float]表示变量a可以是int,也可以是float

扩展:
Any可以表示任意类型,如果写成a: Any那么就相当于没有做任何事,因为如果只写a的话本身就是能代指任意类型

TypeVar

制作模板——自己定义一个类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from typing import TypeVar


T = TypeVar("T")


def give_me_the_bigger_one(num0: T, num1: T) -> T:
if type(num0) != type(num1):
raise ValueError(f"The two number {num0} and {num1} must have the same type!")

if num0 > num1:
return num0
else:
return num1

T = TypeVar("T")表示定义了一个泛型类型变量T,并没有指定他的类型上限,也就是说传给这个函数的参数可以是任意类型

通常在创建泛型类变量时会使用bound参数指定上限:

1
2
3
4
5
6
7
8
from typing import TypeVar

# 定义了一个泛型类型变量T_Config,他的类上限是BasePluginConfig
# 意味着T_Config可以被替代为BasePluginConfig类型或BasePluginConfig的子类
T_Config = TypeVar("T_Config", bound=BasePluginConfig)

class BasePluginConfig():
pass

Generic

类似于where,限制传给一个函数的类型范围

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from typing import List, TypeVar, Generic

T = TypeVar('T')

class Queue(Generic[T]):
def __init__(self) -> None:
# 定义一个元素类型都是T的队列
self.items: List[T] = []

def push(self, item: T) -> None:
self.items.append(item)

def pop(self) -> T:
return self.items.pop(0)

def __str__(self):
return str(self.items)

q = Queue[int]()
q.push(0)
q.push(1)
q.pop() # 移除0
q.push('string') # 并不会报错
print(q)

输出

1
2
3
[1, 'string']

进程已结束,退出代码0

注意最后的q.push('string')'string'虽然并不是int类型,但是在实际运行中并不会报错,不过我们可以通过使用静态类型检查工具(mypy, pytype, pyright, pyre)等方法,提前发现这些错误。这也是推荐使用typing模块来做type annotation的理由之一。

总结

  • 使用typing模块盒注释来提高代码的可读性

  • 将自动化的类型检查工具和API生成工具部署在CI中,方便提前发现错误,方便新人员的理解

参考:

Python中的泛型