简介

Python 的类型提示(Type Hints)是在 Python 3.5 中正式引入的,作为 PEP 484 – Type Hints 的一部分。它是一种在代码中显式声明变量、参数、返回值等的类型信息的方式。它对代码运行没有实际影响,只是作为一种可选的静态类型标注,意在为开发者提供变量、参数和返回值的类型信息,方便代码阅读。

可以把类型提示当作一种注释,就是告诉开发者这个变量“应该”是提示的类型,但是 python 编译器本身不做类型检查,即便代码运行时这个变量不是标注的类型,也不会有任何提示。

普通变量的类型提示

在创建变量时可以进行类型提示

1
2
3
x: int = 1
y: float = 1.0
z: str = "hello"

还可以对一些自定义的数据类型进行提示

1
2
3
4
import numpy
import torch
x: numpy.ndarray = numpy.array([1, 2, 3])
y: torch.tensor = torch.tensor([1, 2, 3])

函数参数和返回值类型

在函数定义中,可以为参数和返回值添加类型提示:

1
2
def add(x: int, y: int) -> int:
return x + y

-> int 表示函数的返回值应该是整数。

这里使用 “应该” 是表示 Python 编译器并不强制用户在调用这个 add 函数时传入非 int 类型的数据,即 Python 编译器不做类型检查,这个类型提示的功能只是作为 “提示”。如果需要进行类型检查还需要额外添加类型检查的语句,例如:

1
2
3
4
def add(x: int, y: int) -> int:
assert isinstance(x, int), f"x is {type(x)}, which must be {int}"
assert isinstance(y, int), f"y is {type(y)}, which must be {int}"
return x + y

list / tuple

直接提示 list / tuple
如果只想说明变量的类型为 list / tuple,不需要额外说明 list / tuple 中元素的类型,可以直接使用 list / tuple 来进行类型提示

1
2
3
4
5
def func1(data: list) -> None:
pass

def func2(data: tuple) -> None:
pass

提示元素的类型
如果还需要说明 list / tuple 中元素的类型,例如 list[int, ...]tuple[str, int] 这种类型的提示,则要分两种情况:

python 3.8 及以下版本中需要从 typing 中引入 List 和 Tuple,才能使用类似 List[int, ...] 这种类型提示

1
2
3
4
5
from typing import List, Tuple
def func1(data: List[int, ...]) -> None:
pass
def func2(data: Tuple[str, int]) -> None:
pass

python 3.9 及以上版本,则可以直接使用 list[int, ...] 进行提示

1
2
3
4
5
# python 3.9+
def func1(data: list[int, ...]) -> None:
pass
def func2(data: tuple[str, int]) -> None:
pass

如果在 3.8 及以下版本中使用 list[int, ...] 或者 tuple[int, str] 这种类型提示则会报如下错误

1
TypeError: 'type' object is not subscriptable

提示元素的个数
List 与 Tuple 本身并不限制它的元素的个数和元素的数据类型,它的元素类型可以是任意的,长度也可以是任意的。而 List[int, ...]Tuple[str, int] 这种类型提示除了提示其中元素的类型还额外提示了元素的个数。

  • List[int] 表示这是一个仅包含单个 int 类型数据的 List,即它的长度为 1;
  • List[int, ...] 表示这是一个仅包含 int 类型数据的 List,它的长度不确定,但是元素类型必须是 int 类型;
  • List[int, str] 表示这个一个仅包含两个元素的长度为 2 的 List,并且它的第一个元素为 int 类型,第二个元素为 str 类型。

list vs tuple
注意,tuple 与 list 的主要区别在于用户不能修改 tuple 中的元素,当你尝试使用赋值语句修改 tuple 中元素的值时,会如下报错

1
TypeError: 'tuple' object does not support item assignment

所以 tuple 可以看作是一种 const 的 list。

dict

dict 类型提示的用法同样在 python 3.8 和 python 3.9+ 中有所区别,下面以 python 3.9+ 进行说明。

提示 data 是一个 key 为 str 类型,value 为 int 类型的字典

1
data: dict[int, str] = {1: "Alice", 2: "Bob"}

还可以使用带有嵌套的写法

1
2
3
4
data: dict[str, dict[str, int]] = {
"group1": {"Alice": 25, "Bob": 30},
"group2": {"Charlie": 35},
}

Union

使用 Union 可以提示多种数据类型。例如

1
2
from typing import Union
data: list[Union[int, str], ...] = [123, "abc", "def", 456]

表示 data 是一个 list,并且其中元素的类型是 int 或者 str。

Any

使用 Any 表示数据类型不做限制。例如

1
2
from typing import Any
data: dict[str, Any] = {"name": "Alice", "age": 25, "scores": [90, 95]}

表示 data 是一个 dict,而其中的 key 需要时 str 类型,而 value 可以时任意类型。

Optional

Optional 作为一种类型标注,用于表示变量可以是某种类型,也可以是 None。例如 Optional[int] 表示被提示变量的数据类型可以是 int 或者是 None。Optional[int]Union[int, None] 这两者是等价的, Optional 本质上是 Union 类型的一种快捷写法。

1
2
3
4
5
6
from typing import Optional, Union

def func(value: Union[int, None]) -> None:
pass
def func(value: Optional[int]) -> None:
pass

Union & |

在 Python 类型提示中,如果希望提示一个变量可以是多种可选类型,例如 intfloat 类型,可以使用 Union(Python 3.9 及以下版本)或 |(Python 3.10 及以上版本)来进行类型注解。

Python 3.10 及以上版本
可以使用 | 运算符来指定多个可选的类型:

1
2
3
def func(value: int | float) -> None:
# value 可以是 int 或 float
pass

Python 3.9 及以下版本
在 Python 3.9 及以下版本中,使用 Union 来指定多种类型:

1
2
3
4
5
from typing import Union

def func(value: Union[int, float]) -> None:
# value 可以是 int 或 float
pass

Literal

Literal 是一种轻量级的值约束,主要用于类型提示,适合限制参数或变量的取值范围,同样它仅仅作为提示,而不进行类型检查,是个君子协定。

1
2
3
4
5
6
7
8
9
10
11
from typing import Literal

def get_color_hex(color: Literal["red", "green", "blue"]) -> str:
if color == "red":
return "#FF0000"
elif color == "green":
return "#00FF00"
elif color == "blue":
return "#0000FF"
else:
raise Exception("Error value")

Literal 可以处理简单的情况,在面临复杂的值约束时,建议使用 Python 的枚举类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from enum import Enum

class Color(Enum):
RED = "red"
GREEN = "green"
BLUE = "blue"

def paint(color: Color) -> None:
if color == Color.RED:
print("Painting red!")
elif color == Color.GREEN:
print("Painting green!")
elif color == Color.BLUE:
print("Painting blue!")

red = Color("red")
paint(red) # Painting red!
green = Color.GREEN
paint(green) # Painting green!
paint(Color.BLUE) # Painting blue!