类型注解

一、 前言:为什么 python 需要“强类型”思维?

Python 是一门动态语言,这意味着你不需要声明变量类型就能运行代码。这种灵活性在写小脚本时很爽,但在构建复杂系统时,隐患无穷:

  1. “猜谜”式开发:看到一个变量 data,你不知道它是 listdict 还是 None
  2. 重构噩梦:修改了一个函数的返回结构,结果全项目到处报 AttributeError
  3. IDE 沦为记事本:没有类型注解,PyCharm 或 VS Code 无法提供精准的自动补全。

类型注解(Type Hints) 是 Python 3.5+ 引入的里程碑功能。它不改变 Python 的动态特性(运行时依然不强制检查),但它通过“元数据”告诉开发工具和框架:这里应该是什么,那里会返回什么。


二、 基础篇:给变量和函数“贴标签”

最基础的语法是在变量名后加 :,在函数返回值前加 ->

1. 基础变量与函数

# 基础变量
user_name: str = "Daoman"
user_id: int = 1001
is_active: bool = True

# 函数输入输出
def get_welcome_msg(name: str, level: int) -> str:
    return f"欢迎 {name}, 您的等级是 {level}"

# 错误示范:静态检查工具(如 mypy)会报错,但 Python 解释器仍能运行
# get_welcome_msg(123, "high") 

三、 进阶篇:容器与复杂结构

在处理列表、字典或多种可能的返回结果时,我们需要用到 typing 模块(Python 3.9+ 推荐使用内置小写类型)。

1. 列表、字典与元组

# Python 3.9+ 推荐写法
prices: list[float] = [299.0, 399.0, 599.0]
user_info: dict[str, int] = {"age": 25, "score": 98}
coordinates: tuple[int, int] = (10, 20)

# 如果是旧版本 (Python < 3.9),需要从 typing 导入
from typing import List, Dict
old_prices: List[float] = [1.1, 2.2]

2. 联合类型(Union)与可选类型(Optional)

在爬虫抓取数据时,某个字段可能存在,也可能是 None;或者某个 ID 既可以是数字也可以是字符串。

from typing import Union, Optional

# 3.10 之前的写法
def fetch_data(uid: Union[int, str]) -> Optional[str]:
    # 返回 str 或者 None
    return "some data" if uid else None

# 3.10+ 推荐写法 (使用 | 符号,极其简洁)
def fetch_data_v2(uid: int | str) -> str | None:
    return "data" if uid else None

四、 核心篇:类、模型与 Callable

1. 函数作为参数 (Callable)

在 python 开发中,经常需要传递回调函数或处理函数。

from typing import Callable

# 标注一个接收两个 int 并返回 int 的函数
def apply_op(a: int, b: int, func: Callable[[int, int], int]) -> int:
    return func(a, b)

apply_op(1, 2, lambda x, y: x + y)

2. Pydantic 模型(FastAPI 的灵魂)

这是你在开发后端时最常用的场景。类型注解不仅是注释,还是数据校验规则

from pydantic import BaseModel

class Course(BaseModel):
    id: int
    title: str
    price: float
    is_published: bool = False # 带有默认值的注解

def update_course(item: Course):
    # FastAPI 会根据 Course 的注解自动验证前端传来的 JSON
    print(f"正在更新课程: {item.title}")

五、 工程实践:别让注解成为负担

  1. 不要过度注解:局部变量(如循环里的 i)通常不需要注解,IDE 能自动推断。
  2. 使用 Any 作为逃生舱:当你实在无法确定类型(比如解析极复杂的 JS 逆向数据)时,使用 from typing import Any,告诉检查工具“放我一马”。
  3. 配合 mypy 检查:在终端运行 mypy your_script.py,它会扫描全项目并指出所有类型不匹配的潜在 Bug。

结语

类型注解是 Python 从“脚本语言”向“工业语言”跨越的关键。掌握了它,你的代码将从“能跑就行”变成“清晰、健壮、易于维护”。