摘要:在本教程中,你将学习 Python 类型提示以及如何使用 mypy 工具进行静态类型检查。
Python 类型提示介绍
一些编程语言具有静态类型,例如 C/C++。这意味着你需要提前声明变量、参数以及函数返回值的类型。预定义的类型允许编译器在编译和运行程序之前检查代码。
Python 使用动态类型,其中函数的变量、参数和返回值可以是任何类型。此外,变量的类型可以在程序运行时改变。
一般来说,动态类型使得编程变得容易,但会导致一些只有在程序运行时才能发现的意外错误。
Python 的类型提示为你提供了可选的静态类型,以充分利用静态类型和动态类型的优点。
以下示例定义了一个简单的函数,该函数接受一个字符串并返回另一个字符串:
def say_hi(name):
return f'Hi {name}'
greeting = say_hi('John')
print(greeting)
以下是给函数的参数和返回值添加类型提示的语法:
parameter: type
-> type
例如,以下展示了如何为 say_hi()
函数的 name
参数和返回值使用类型提示:
def say_hi(name: str) -> str:
return f'Hi {name}'
greeting = say_hi('John')
print(greeting)
输出:
Hi John
在这种新语法中name
参数的类型为 str
:
name: str
并且 say_hi()
函数的返回值也具有 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
要检查类型提示的语法,你需要使用一个静态类型检查工具。
使用静态类型检查工具:mypy
Python 没有官方的静态类型检查工具。目前,最流行的第三方工具是 Mypy。由于 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
的值是一个字符串字面量,因此静态类型检查器会推断 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
)模块为多种类型设置类型提示。
首先,从 typing
模块导入 Union
:
from typing import Union
第二,使用 Union
创建一个包含 int
和 float
的联合类型:
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()
函数中使用了 Number
别名。
为列表、字典和集合添加类型提示
你可以使用以下内置类型为列表、字典和集合设置类型提示:
list
dict
set
如果你在一个变量中类型提示为列表,但后来为其分配了一个字典,你会得到一个错误:
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
模块中的类型别名:
例如,以下定义了一个整数列表:
from typing import List
ratings: List[int] = [1, 2, 3]
None
类型
如果一个函数没有显式地返回值,你可以使用 None
来对返回值进行类型提示。例如:
def log(message: str) -> None:
print(message)
字面量类型
如果你希望一个变量或函数参数接受特定的字面量值列表,你可以使用 Literal
类型。例如:
from typing import Literal
def set_mode(mode: Literal['read', 'write']) -> None:
print(f"Setting mode to {mode}")
set_mode('write')
工作原理:
首先,从 typing
模块导入 Literal
:
from typing import Literal
其次,定义 set_mode
函数,该函数接受一个类型为 Literal
的参数 modemode
只能接受两个字符串字面量之一,即 'read'
或 'write'
:
mode: Literal['read', 'write']
第三,调用 set_mode
函数并传递 'write'
参数:
set_mode('write')
如果你运行 mypy
命令,它将显示输出:
Success: no issues found in 1 source file
如果你将参数更改为不是 'read'
或 'write'
的值mypy
将会报错:
set_mode('execute')
错误:
main.py:8: error: Argument 1 to "set_mode" has incompatible type "Literal['execute']"; expected "Literal['read', 'write']" [arg-type]
Found 1 error in 1 file (checked 1 source file)
Final变量
你可以给一个最终变量(final variable)赋值一次。如果你再次给它赋值,将会遇到错误。最终变量有助于防止你意外修改常量。例如:
from typing import Final
HTTPS : Final[bool] = True
print(HTTPS )
工作原理:
首先,从 typing
模块导入 Final
:
from typing import Final
其次,定义一个类型为 Final[bool]
的常量 HTTPS
,并将其值设置为 True
:
HTTPS : Final[bool] = True
除了 bool
类型,你还可以使用其他内置类型,如 int
、float
、str
等,来存储各种类型的最终值。
第三,显示 HTTPS
的值:
print(HTTPS)
mypy
会将该程序标记为成功。
如果你尝试将 HTTPS
改为 False
,类型检查器mypy
会将该赋值操作标记为错误。
from typing import Final
HTTPS : Final[bool] = True
HTTPS = False # error
错误:
main.py:4: error: Cannot assign to final name "HTTPS" [misc]
Found 1 error in 1 file (checked 1 source file)
总结
使用类型提示(type hints)和静态类型检查工具,使你的代码更加健壮。