本篇我们介绍 Python 类型提示(type hint)功能,以及如何使用 mypy 工具执行静态类型检查。
许多编程语句使用静态类型,例如 C/C++。静态类型意味着我们需要在使用之前声明变量、函数参数以及返回值的类型。预定义的类型使得编译器可以在编译和运行程序之前检查代码。
Python 使用动态类型,变量、函数参数以及返回值可以是任何类型。同时,变量的类型在程序运行期间可以改变。
一般来说,动态类型更容易编写代码,但是也可能会引起预料之外的错误,这些错误只有在程序运行时才会被发现。
Python 的类型提示功能提供了可选的静态类型,可以同时获得静态类型和动态类型的优点。以下示例定义了一个简单的函数,可以接受一个字符串参数并返回另一个字符串:
def say_hi(name): return f'Hi {name}' greeting = say_hi('John') print(greeting)
以下是为函数参数和返回值添加类型提示的语法:
parameter: type -> type
以下示例在 say_hi() 函数中使用了类型提示:
def say_hi(name: str) -> str: return f'Hi {name}' greeting = say_hi('John') print(greeting)
输出结果如下:
Hi John
在新的函数定义中,参数 name 的数据类型为 str:
name: str
函数返回值的类型也是 str:
-> str
除了 str 类型之外,我们也可以使用其他的内置类型,例如 int、float、bool 以及 bytes。
注意,Python 解释器会直接忽略类型提示。如果我们将一个数字作为参数传递给 say_hi() 函数,程序可以正常执行,而不会返回警告或者错误:
def say_hi(name: str) -> str: return f'Hi {name}' greeting = say_hi(123) print(greeting)
输出结果如下:
Hi 123
为了检查类型提示中的语法,我们需要一个静态类型检查工具。
Python 没有提供官方的静态类型检查工具。目前最流行的第三方工具是 Mypy,我们需要使用 pip 命名进行安装:
pip instal mypy
安装之后,我们可以使用 mypy 检查代码中的类型提示:
mypy app.py
显示的信息如下:
app.py:5: error: Argument 1 to "say_hi" has incompatible type "int"; expected "str" Found 1 error in 1 file (checked 1 source file)
以上错误表示 say_hi 函数的实际参数是 int 类型,但实际需要一个 str 类型的参数。
如果我们将参数改回字符串之后再次运行 mypy,将会显示以下成功信息:
Success: no issues found in 1 source file
当我们定义变量时,可以增加一个类型提示,例如:
name: str = 'John'
变量 name 的类型为 str。如果我们一个非字符串的值赋予变量 name,静态类型检查器将会返回错误。例如:
name: str = 'Hello' name = 100 app.py:2: error: Incompatible types in assignment (expression has type "int", variable has type "str") Found 1 error in 1 file (checked 1 source file)
通常不需要为变量指定一个类型,因为静态类型检查器可以基于变量的值推断它的类型。以下示例中,name 的值时一个字符串常量,因此静态类型检查器推断它的类型为 str:
name = 'Hello' name = 100
程序同样会返回类型错误:
app.py:2: error: Incompatible types in assignment (expression has type "int", variable has type "str") Found 1 error in 1 file (checked 1 source file)
以下 add() 函数返回了两个数字的求和:
def add(x, y): return x + y
函数的参数可以是整数或浮点数。为了定义支持多种类型的类型提示,可以使用 typing 模块中的 Union 函数。
首先,导入 typing 模块中的 Union 函数:
from typing import Union
其次,使用 Union 创建一个包含 int 和 float 的联合类型(union type):
def add(x: Union[int, float], y: Union[int, float]) -> Union[int, float]: return x + y
以下是完整的代码:
from typing import Union def add(x: Union[int, float], y: Union[int, float]) -> Union[int, float]: return x + y
从 Python 3.10 开始,我们可以使用 X | Y 语法创建联合类型,例如:
def add(x: int | float, y: int | float) -> int | float: return x + y
Python 允许为数据类型指定别名,然后在类型提示中使用别名。例如:
from typing import Union number = Union[int, float] def add(x: number, y: number) -> number: return x + y
以上示例中,我们为联合类型 Union[int, float] 指定了一个别名 Number,然后在 add() 函数中使用了该别名。
我们可以使用以下内置类型作为列表、字典以及集合的类型提示:
如果我们为变量指定了 list 类型提示,然后为它指定一个字典值,将会返回错误:
ratings: list = [1, 2, 3] ratings = {1: 'Bad', 2: 'average', 3: 'Good'}
静态类型检查返回的错误如下:
app.py:3: error: Incompatible types in assignment (expression has type "Dict[int, str]", variable has type "List[Any]") Found 1 error in 1 file (checked 1 source file)
我们可以使用 typing 模块中的以下类型别名为列表、字典以及集合中的值指定类型:
类型别 | 内置类型 |
---|---|
List | 列表 |
Tuple | 元组 |
Dict | 字典 |
Set | 集合 |
Frozenset | 不可变集合 |
Sequence | 列表、元组以及其他序列数据类型 |
Mapping | 字典(dict)、集合、不可变集合以及其他映射数据类型 |
ByteString | bytes、bytearray以及memoryview |
例如,以下代码定义了一个整数列表:
from typing import List ratings: List[int] = [1, 2, 3]
如果函数没有显式的返回值,可以使用 None 作为返回值的类型提示。例如:
def log(message: str) -> None: print(message)