在 Python
中,Type Hints
一般可以翻译为以下几种方式,它们基本上是同一个概念:
无论你使用哪种翻译,意思都是指 Python 代码中对变量、参数、返回值等添加的类型信息,以便进行静态检查和提高可读性
鄙人习惯称其为 类型提示
Python
是动态类型语言,变量的类型是在运行时确定的。因此需要静态类型检查工具的的帮助让我们检查代码,发现运行前潜在的类型错误,减少运行时错误
常见的 Python
静态类型检查工具
本篇博文主要介绍 mypy
示例
uv add mypy
def add(x: int, y: int) -> int: # 这里的 int 就是类型提示
return x +
add("x", 1)
mypy main.py
main.py:5: error: Argument 1 to "add" has incompatible type "str"; expected "int" [arg-type]
Found 1 error in 1 file (checked 1 source file)
可以看到这里函数输入一个字符串,但是期望的是一个整型,这里 mypy
就会提示了。
在没有类型提示的情况下,阅读代码时,我们无法直接知道变量或函数参数的类型,可能会写出错误的代码。
def add(a, b):
return a + b
使用类型提示
def add(a: int, b: int) -> int:
return a + b
a
和 b
预期是 int
Python
是动态类型语言,如果没有类型检查,错误往往会在运行时才暴露
def greet(name):
return "Hello, " + name
print(greet(42)) # 运行时报错:TypeError: can only concatenate str (not "int") to str
使用类型检查工具(如 mypy
)可以提前发现错误
当代码有类型注解时,IDE(如 PyCharm、VS Code)可以提供
在大型项目中,不同开发者可能会对变量的类型理解不同:
def process_data(data):
...
data
是 list[str]
还是 dict
?data
的类型,可能会误用。from typing import List
def process_data(data: List[str]) -> None:
...
许多静态语言(如 Java、C++)都有强类型检查,可以
Python
没有强制类型检查,但 type hints
+ mypy
让 Python
部分具备静态语言的优势,且仍然保持灵活性
uv add mypy
uv 是一个超快的 Python 包和解释器管理工具,如果你不了解,可以看下面的这篇博文,或者直接使用 pip
安装也可以
Python 项目管理利器:uv 入门指南
def add(a: int, b: int) -> int:
return a + b
result = add(3, "hello") # ❌ 传入了 str 而不是 int
使用命令行运行 mypy
uv run mypy main.py
报错
ain.py:4: error: Argument 2 to "add" has incompatible type "str"; expected "int" [arg-type]
Found 1 error in 1 file (checked 1 source file)
期待一个整型,但是输入的是 str
int
float
None
str
bool
bytes
Python
提供了 List、Tuple、Set、Dict 等数据结构,在 MyPy
中需要显式标注内部元素的类型。
from typing import List, Tuple, Dict, Set
numbers: List[int] = [1, 2, 3]
coordinates: Tuple[float, float] = (10.5, 20.3)
student_ages: Dict[str, int] = {"Alice": 25, "Bob": 22}
unique_ids: Set[int] = {1, 2, 3, 4}
注意:Python 3.9
以后,可以直接使用 list[int]
、dict[str, int]
等简化写法,而不需要 typing.List
有时候,一个函数的返回值可能是字符串也可能是 None
这时候我们就可以使用 Optional
注解
举例
from typing import Optional
def find_user(user_id: int) -> Optional[str]:
if user_id == 1:
return "Alice"
return None
Optional
表示参数可以是 None
相同写法(python3.10
及以上)
def find_user(user_id: int) -> str | None:
Union
类型表示变量可以是多个可能的类型
from typing import Union
value: Union[int, str] = 42
value = "hello" # ✅ 允许
相同写法(python3.10
及以上)
value: int | str = 42
Any
表示可以是任意类型,MyPy
不会检查它。
from typing import Any
def dynamic_function(data: Any) -> Any:
return data
Final
是用于类型注解的一个特殊标记,表示某个变量、属性或方法的值是不可变的(即不可重新赋值或覆盖)。它通常用于标记常量或不可更改的值。
from typing import Final
class Constants:
PI: Final = 3.14159 # 不能被修改
Constants.PI = 2.2
# main.py:8: error: Cannot assign to final attribute "PI" [misc]
# Found 1 error in 1 file (checked 1 source file)
Callable
用于标注函数或可调用对象的类型。它可以明确函数的参数类型和返回值类型,使代码更具可读性和安全性。
用法
Callable[[参数类型1, 参数类型2, ...], 返回值类型]
举例
from typing import Callable
# 定义一个函数,接受两个 int 参数,返回一个 int
def add(x: int, y: int) -> int:
return x + y
# 使用 Callable 标注函数类型
math_operation: Callable[[int, int], int] = add
# 调用函数
result = math_operation(10, 20)
print(result) # 输出: 30
# 使用 Callable 标注 lambda 函数
square: Callable[[int], int] = lambda x: x ** 2
# 调用函数
print(square(5)) # 输出: 25
Iterator
迭代器是一个实现了 __iter__
和 __next__
方法的对象。typing
模块提供了 Iterator[T]
类型,用于标注迭代器的元素类型。
from typing import Iterator
# 自定义迭代器类
class CountUpTo:
def __init__(self, max_value: int):
self.max_value = max_value
self.current = 0
def __iter__(self) -> Iterator[int]:
return self
def __next__(self) -> int:
if self.current >= self.max_value:
raise StopIteration
self.current += 1
return self.current
# 使用自定义迭代器
counter: Iterator[int] = CountUpTo(5)
for num in counter:
print(num)
# 输出:
# 1
# 2
# 3
# 4
# 5
Generator
生成器是一种特殊的迭代器,使用 yield
关键字生成值。typing
模块提供了 Generator[Y, S, R]
类型,用于标注生成器的产出类型(Y
)、发送类型(S
)和返回类型(R
)
from typing import Generator
# 定义一个生成器,产出 int,发送 None,返回 str
def count_down_from(n: int) -> Generator[int, None, str]:
while n > 0:
yield n
n -= 1
return "Done"
# 使用生成器
counter: Generator[int, None, str] = count_down_from(3)
# 获取生成器的返回值
while True:
try:
num = next(counter)
print(num)
except StopIteration as e:
print(e.value) # 输出: Done
break
# 输出:
# 3
# 2
# 1
# Done
Sequence
表示有序的、可迭代的容器,但不要求可变。
from typing import Sequence
def print_elements(elements: Sequence[int]) -> None:
for element in elements:
print(element)
# 使用 list
print_elements([1, 2, 3])
# 输出:
# 1
# 2
# 3
# 使用 tuple
print_elements((4, 5, 6))
# 输出:
# 4
# 5
# 6
# 使用 str(如果元素类型是 str)
def print_chars(chars: Sequence[str]) -> None:
for char in chars:
print(char)
print_chars("abc")
# 输出:
# a
# b
# c
Literal
用于限制变量或参数只能取 特定的值,提高类型安全性。
from typing import Literal
def set_status(status: Literal["pending", "approved", "rejected"]) -> None:
print(f"订单状态设置为: {status}")
set_status("pending") # ✅ 合法
set_status("approved") # ✅ 合法
set_status("rejected") # ✅ 合法
# set_status("other") # ❌ 报错:只能传入 "pending"、"approved" 或 "rejected"
Python 的泛型(Generics)是一种 类型参数化 机制,它允许我们编写通用代码,使函数、类或数据结构适用于 多种类型,提高 代码复用性 和 类型安全性
简单示例
from typing import TypeVar
T = TypeVar("T") # 定义泛型类型 T
def first_element(items: list[T]) -> T:
return items[0]
print(first_element([1, 2, 3])) # int -> 1
print(first_element(["a", "b", "c"])) # str -> "a"
函数first_element
可以接收两种类型的列表
定义泛型类
from typing import Generic
T = TypeVar("T") # 泛型类型 T
class Box(Generic[T]):
def __init__(self, content: T):
self.content = content
def get_content(self) -> T:
return self.content
int_box = Box(100) # T = int
str_box = Box("hello") # T = str
print(int_box.get_content()) # 100
print(str_box.get_content()) # "hello"
MyPy
允许你通过配置文件来定制类型检查的行为,从而更好地适应你的项目需求。配置文件通常是 .ini
格式,名为 mypy.ini
或 setup.cfg
,并且可以用来控制 MyPy 的检查规则、严格性等
你也可以使用 uv 的配置文件 pyproject.toml
配合 mypy 使用,这样就不用写 mypy.ini 文件了
[project]
name = "app"
version = "0.1.0"
description = ""
requires-python = ">=3.10,<4.0"
dependencies = [
]
[tool.uv]
dev-dependencies = [
"mypy<2.0.0,>=1.8.0"
]
[tool.mypy]
# 启用严格模式
strict = true
# 指定要检查的文件或目录
files = "main.py"
# 忽略缺少类型注解的导入
ignore_missing_imports = true
# 显示错误代码,便于查找问题
show_error_codes = true
# 排除某些文件或目录的检查
exclude = "^tests/.*" # 排除 tests 目录中的文件
# 指定使用的 Python 版本
python_version = 3.13
uv run mypy