类型注解概述

类型注解是在代码中为变量、函数参数和返回值标明期望的数据类型,提高代码可读性并借助工具进行静态检查

Python 是一门动态类型语言:变量可以在运行时改变类型,不需要提前声明。这在带来灵活性的同时,也带来了隐患——类型错误只能等到程序运行时才能被发现。

类型注解的出现解决了这个问题:

1
2
3
4
5
6
7
# 没有类型注解:不知道 x 应该是什么类型
def double(x):
return x * 2

# 有类型注解:一眼就能看出 x 应该是数字
def double(x: int) -> int:
return x * 2

类型注解的好处:

提高可读性 — 一看就知道变量/函数期望什么类型
编辑器智能提示 — IDE 能根据类型提供自动补全和错误提示
静态类型检查 — 使用 mypy 等工具在运行前发现类型错误
更好的文档 — 类型信息本身就是一种精确的代码文档

注意:Python 的类型注解在运行时不会强制检查——它只是一种"标注",不会影响程序的执行。

变量的类型注解

在变量名后使用 : 类型 的语法标注变量的期望类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
"""
变量类型注解语法:
变量名: 类型 = 值
"""

# 基础类型注解
name: str = "PyTs1n9"
age: int = 25
height: float = 1.75
is_student: bool = True

# 可以直接写注解而不赋值(声明变量类型)
title: str # 仅声明类型,没有初始化

# 查看类型注解(运行时注解存在 __annotations__ 中)
print(__annotations__)
# 输出 {'name': <class 'str'>, 'age': <class 'int'>, ...}
1
2
3
4
# 类型注解不会在运行时强制检查
x: int = "hello" # Python 不会报错!
print(type(x)) # 输出 <class 'str'>
# 需要借助 mypy 等静态检查工具来发现这类错误

函数的类型注解

为函数的参数和返回值添加类型注解,是类型注解最常用也最有价值的场景

  1. 参数与返回值注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
"""
函数类型注解语法:
def 函数名(参数: 类型, ...) -> 返回类型:
pass
"""

# 参数和返回值都有注解
def greet(name: str, times: int) -> str:
"""重复问好"""
return f"你好 {name}!" * times

result = greet("PyTs1n9", 3)
print(result) # 输出 你好 PyTs1n9!你好 PyTs1n9!你好 PyTs1n9!

# 没有返回值用 None
def log_message(msg: str) -> None:
"""只打印,不返回"""
print(f"[LOG] {msg}")
1
2
3
4
5
6
7
# 参数可以有默认值
def create_user(name: str, age: int = 18, active: bool = True) -> dict:
return {"name": name, "age": age, "active": active}

# 有默认值时类型注解在 = 前面
print(create_user("小明"))
# 输出 {'name': '小明', 'age': 18, 'active': True}
  1. 获取函数注解信息
1
2
3
4
5
6
7
8
# 使用 __annotations__ 属性查看函数的类型注解
def add(a: int, b: int) -> int:
return a + b

print(add.__annotations__)
# 输出 {'a': <class 'int'>, 'b': <class 'int'>, 'return': <class 'int'>}

# 这在一些框架和库中用于自动处理参数类型(如 FastAPI)

复合类型注解

使用 typing 模块中的工具来注解列表、字典、元组等复合容器类型

  1. 列表、字典、元组、集合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from typing import List, Dict, Tuple, Set

# 列表:List[元素类型]
def get_scores(students: List[str]) -> List[int]:
"""返回学生对应的分数列表"""
return [len(name) * 10 for name in students]

scores: List[int] = [85, 92, 78, 90]

# 字典:Dict[键类型, 值类型]
def build_info(name: str, age: int) -> Dict[str, int]:
return {name: age}

config: Dict[str, str] = {"host": "localhost", "port": "8080"}

# 元组:Tuple[类型1, 类型2, ...]
def get_position() -> Tuple[float, float]:
"""返回坐标 (x, y)"""
return (3.14, 2.72)

# 集合:Set[元素类型]
ids: Set[int] = {1, 2, 3, 4}
  1. Python 3.9+ 的新语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"""
Python 3.9 之后,可以直接使用内置的 list、dict、tuple、set 来注解
不需要从 typing 导入 List、Dict 等(这些已被弃用)
"""

# Python 3.9+ 推荐写法(更简洁)
def get_scores(students: list[str]) -> list[int]:
return [len(name) * 10 for name in students]

def build_info(name: str, age: int) -> dict[str, int]:
return {name: age}

config: dict[str, str] = {"host": "localhost"}

# 注:低版本 Python(< 3.9)仍需从 typing 导入
# from typing import List, Dict, Tuple, Set
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 嵌套复合类型
from typing import List, Dict

# 列表嵌套列表 — 二维矩阵
matrix: List[List[int]] = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]

# 字典嵌套列表 — 班级学生
classes: Dict[str, List[str]] = {
"一班": ["小明", "小红", "小刚"],
"二班": ["小王", "小李", "小张"]
}

# 字典嵌套字典 — 学生成绩
grades: Dict[str, Dict[str, int]] = {
"小明": {"语文": 85, "数学": 92},
"小红": {"语文": 90, "数学": 88}
}

Union 与 Optional

Union 表示多种类型中的一种,Optional 是 Union[X, None] 的简写

  1. Union 类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
"""
Union[类型1, 类型2, ...] 表示参数可以是多种类型中的一种
适用场景:一个参数可以接受不同类型的值
"""
from typing import Union

# age 可以是 int 或 str
def show_age(age: Union[int, str]) -> str:
"""显示年龄"""
return f"年龄:{age} 岁"

print(show_age(18)) # 输出 年龄:18 岁
print(show_age("十八")) # 输出 年龄:十八 岁

# 多个类型
def process(data: Union[int, float, str]) -> str:
"""处理不同类型的数据"""
if isinstance(data, str):
return f"字符串:{data}"
return f"数字:{data}"
1
2
3
4
5
6
7
8
9
# Python 3.10+ 可以用 | 代替 Union(更简洁)
# def show_age(age: int | str) -> str:
# return f"年龄:{age} 岁"

# def process(data: int | float | str) -> str:
# return str(data)

# 注:| 语法需要 Python 3.10+
# 低版本仍用 Union[int, str]
  1. Optional 类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
"""
Optional[类型] 等价于 Union[类型, None]
表示参数可以是某种类型,也可以是 None
"""
from typing import Optional

# 昵称是可选的(可能为 None)
def greet(name: str, nickname: Optional[str] = None) -> str:
if nickname:
return f"{name}{nickname}),你好!"
return f"{name},你好!"

print(greet("张三")) # 输出 张三,你好!
print(greet("张三", nickname="三哥")) # 输出 张三(三哥),你好!

# 等价写法
def greet2(name: str, nickname: Union[str, None] = None) -> str:
pass

# Python 3.10+ 更简洁的写法
# def greet3(name: str, nickname: str | None = None) -> str:
# pass
1
2
3
4
5
6
7
8
# Optional 的常见应用场景
def find_user(user_id: int) -> Optional[dict]:
"""查找用户,找不到返回 None"""
users = {1: {"name": "小明"}, 2: {"name": "小红"}}
return users.get(user_id) # 可能返回 dict 或 None

result = find_user(3)
print(result) # 输出 None

特殊类型注解

typing 模块提供了 Any、Callable、Literal 等特殊类型,处理更复杂的类型场景

  1. Any — 任意类型
1
2
3
4
5
6
7
8
9
10
11
12
13
"""
Any 表示任意类型,相当于"不进行类型检查"
适合确实无法确定类型或逐步引入注解的场景
"""
from typing import Any

# 可以接受任何类型的参数
def debug_print(value: Any) -> None:
print(f"[DEBUG] {type(value).__name__}: {value}")

debug_print(42) # 输出 [DEBUG] int: 42
debug_print("hello") # 输出 [DEBUG] str: hello
debug_print([1, 2, 3]) # 输出 [DEBUG] list: [1, 2, 3]
  1. Callable — 可调用对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
"""
Callable[[参数类型, ...], 返回类型] 表示一个可调用对象(函数)
"""
from typing import Callable

# register_callback 接受一个函数作为参数
def register_callback(callback: Callable[[str], None]) -> None:
"""注册回调函数"""
callback("数据加载完成")

def on_complete(msg: str) -> None:
print(f"收到通知:{msg}")

register_callback(on_complete) # 输出 收到通知:数据加载完成
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Callable 的完整示例:高阶函数
from typing import Callable

def apply_twice(func: Callable[[int], int], value: int) -> int:
"""将函数应用两次"""
return func(func(value))

def add_three(x: int) -> int:
return x + 3

print(apply_twice(add_three, 10)) # 输出 16(10+3+3)

# apply_twice 的注解含义:
# func: Callable[[int], int] → 接受一个 int,返回一个 int 的函数
# value: int → 整数值
# -> int → 返回整数
  1. Literal — 字面量类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"""
Literal 限制变量只能取指定的几个值
适合表示状态、模式等有限选项
"""
from typing import Literal

# mode 只能是这三个值
def set_mode(mode: Literal["read", "write", "append"]) -> str:
return f"当前模式:{mode}"

print(set_mode("read")) # 输出 当前模式:read
# set_mode("delete") # 静态检查工具会报警告

# 方向控制
def move(direction: Literal["上", "下", "左", "右"], step: int) -> None:
print(f"向{direction}移动了{step}步")

类型别名与自定义类型

通过类型别名给复杂的类型注解起一个简短的名字,提高可读性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
"""
类型别名:将复杂的类型组合赋值给一个有意义的名称
"""

# 没有别名时:类型注解冗长难读
def process_scores(scores: dict[str, list[int]]) -> dict[str, float]:
pass

# 定义类型别名(语义清晰)
StudentScores = dict[str, list[int]] # 学生 → 成绩列表
Averages = dict[str, float] # 学生 → 平均分

def process_scores(scores: StudentScores) -> Averages:
"""计算每个学生的平均分"""
return {name: sum(sc) / len(sc) for name, sc in scores.items()}

# 使用
scores: StudentScores = {
"小明": [85, 92, 78],
"小红": [90, 88, 95]
}
print(process_scores(scores))
# 输出 {'小明': 85.0, '小红': 91.0}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 常见的类型别名场景
from typing import Union, List, Dict

# 坐标
Point = tuple[float, float]

# 学生信息
StudentInfo = dict[str, Union[str, int, list[int]]]

# 回调函数
Callback = callable[[str], None]

# 矩阵
Matrix = list[list[int]]

# 定义使用类型别名的函数
def distance(p1: Point, p2: Point) -> float:
"""计算两点间的距离"""
import math
return math.sqrt((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2)

类型检查工具

类型注解本身不强制检查,需要借助 mypy 等静态类型检查工具来发挥作用

  1. mypy 的使用
1
2
3
4
5
6
7
8
"""
mypy 是 Python 最常用的静态类型检查工具
安装和基本使用(终端中):
"""

# pip install mypy # 安装 mypy
# mypy script.py # 检查单个文件
# mypy project/ # 检查整个项目
1
2
3
4
5
6
7
8
9
10
11
# 示例:准备一个带类型错误的文件 test_types.py
"""
文件 test_types.py:
"""
def add(a: int, b: int) -> int:
return a + b

# 错误:传入字符串但注解要求 int
result = add("hello", 5)
# mypy 检查会报告:
# error: Argument 1 to "add" has incompatible type "str"; expected "int"
  1. IDE 集成的好处
1
2
3
4
5
6
7
8
9
10
11
12
"""
即使不运行 mypy,类型注解也能提升开发体验:
"""

# VS Code / PyCharm 等 IDE 会根据类型注解提供:
# 1. 自动补全:输入变量名. 后提示可用的方法
# 2. 错误提示:类型不匹配时显示红色波浪线
# 3. 跳转定义:点击类型名直接跳转到定义处

# 示例:IDE 知道 name 是 str,自动提示 .upper() .lower() .split() 等
def shout(name: str) -> str:
return name.upper() + "!!!" # IDE 自动补全 .upper()

综合练习

将类型注解知识融会贯通,在实际场景中体验带类型注解的代码带来的清晰度提升

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
"""
练习一:学生成绩管理系统(带完整类型注解)
"""
from typing import Optional

# 类型别名
StudentRecord = dict[str, object] # 学生记录
ScoreList = list[dict[str, int]] # 成绩列表

class GradeManager:
"""成绩管理器"""

def __init__(self, class_name: str) -> None:
self.class_name: str = class_name
self.students: list[StudentRecord] = []

def add_student(
self, name: str, age: int, scores: dict[str, int]
) -> None:
"""添加学生"""
total = sum(scores.values())
avg = total / len(scores) if scores else 0
self.students.append({
"name": name,
"age": age,
"scores": scores,
"total": total,
"avg": avg
})
print(f"学生 {name} 已添加(平均分:{avg:.1f})")

def find_student(self, name: str) -> Optional[StudentRecord]:
"""查找学生,找不到返回 None"""
for stu in self.students:
if stu["name"] == name:
return stu
return None

def get_top_students(self, top_n: int = 3) -> list[StudentRecord]:
"""获取平均分最高的 N 名学生"""
sorted_students = sorted(
self.students,
key=lambda s: s["avg"], # type: ignore
reverse=True
)
return sorted_students[:top_n]

def show_report(self) -> None:
"""打印班级报表"""
print(f"\n===== {self.class_name} 成绩报表 =====")
for i, stu in enumerate(self.get_top_students(len(self.students)), 1):
print(
f"{i}. {stu['name']} — "
f"总分:{stu['total']},平均:{stu['avg']:.1f}"
)

# 使用
# manager = GradeManager("Python 进修班")
# manager.add_student("小明", 18, {"语文": 85, "数学": 92, "英语": 78})
# manager.show_report()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
"""
练习二:带类型注解的工具函数库
"""
from typing import Callable

# 带注解的通用工具函数

def safe_divide(a: float, b: float, default: float = 0.0) -> float:
"""安全除法,除数为零时返回默认值"""
return a / b if b != 0 else default

def filter_list(
items: list[int], condition: Callable[[int], bool]
) -> list[int]:
"""按条件过滤列表"""
return [item for item in items if condition(item)]

def group_by_key(
items: list[dict[str, object]], key: str
) -> dict[str, list[dict[str, object]]]:
"""按指定键对字典列表进行分组"""
result: dict[str, list[dict[str, object]]] = {}
for item in items:
k = str(item.get(key, ""))
if k not in result:
result[k] = []
result[k].append(item)
return result

# 测试
print(safe_divide(10, 2)) # 输出 5.0
print(safe_divide(10, 0)) # 输出 0.0

nums = [1, 2, 3, 4, 5, 6]
even = filter_list(nums, lambda x: x % 2 == 0)
print(even) # 输出 [2, 4, 6]

类型注解是 Python 从"动态灵活"走向"工程可靠"的桥梁。从变量到函数、从简单类型到 Union 和 Optional、从内置容器到类型别名,每个注解都在让代码更加清晰可维护。结合 mypy 和 IDE 的类型检查能力,你能在编码阶段就发现大量潜在的类型错误——而不必等到程序崩溃时再排查。记住:Python 不会在运行时强制执行注解,它是对人(以及 IDE 和检查工具)的承诺,而不是对解释器的指令。

Python Study Note 系列文章

  1. Python Study Note
  2. Python 函数使用(四)
  3. Python 判断语句(二)
  4. Python 基础语法(一)
  5. Python 异常处理(七)
  6. Python 循环语句(三)
  7. Python 数据容器(五)
  8. Python 文件基础操作(六)
  9. Python 模块与包(八)
  10. Python 类型注解(十)
  11. Python 面向对象(九)重点
  12. Python 高阶技巧(十一)