人人都会AI编程

23.4 类型提示(Type Hints):变量、函数、类的类型标注

更新时间:2026-07-12

Python 是动态类型语言,变量本身没有固定类型,但自从 Python 3.5 引入类型提示(Type Hints)后,我们可以自愿地给代码加上类型标注。它们不影响运行时,却能让代码意图更清晰,配合静态检查工具(如 mypy、pyright)能在运行前发现大量类型错误。

变量类型标注

最简单的标注:在变量名后加冒号和类型。

name: str = "Alice"
age: int = 30
is_active: bool = True

对于容器类型,早期推荐从 typing 导入:

from typing import List, Dict, Tuple, Set, Optional, Union

scores: List[int] = [85, 92, 78]
user: Dict[str, str] = {"id": "001", "name": "Bob"}
point: Tuple[float, float] = (3.5, 7.2)
unique_ids: Set[int] = {1, 2, 3}
maybe_error: Optional[str] = None    # str 或 None
data: Union[int, str] = "hello"       # int 或 str

Python 3.9 开始,可以直接用内置的 listdict 等作为泛型,不再需要从 typing 导入 List 等:

scores: list[int] = [85, 92, 78]
user: dict[str, str] = {"id": "001", "name": "Bob"}

Python 3.10 又引入了更简洁的联合类型语法:X | Y 代替 Union[X, Y]X | None 代替 Optional[X]

data: int | str = "hello"
maybe_error: str | None = None

函数类型标注

函数可以标注参数类型和返回值类型(用 -> 标识):

def greet(name: str, times: int = 1) -> str:
    return f"Hello, {name}! " * times

当参数可以接受多种类型或 None 时,使用联合类型:

def process(item: int | str) -> str:
    return str(item)

对于没有返回值的函数(返回 None),应显式标注 -> None

def log_message(msg: str) -> None:
    print(msg)

接受函数作为参数时,用 Callable

from typing import Callable

def apply(func: Callable[[int, int], int], x: int, y: int) -> int:
    return func(x, y)

如果函数参数或返回值类型不确定,可用 Any(意为“任意类型”,关闭该部分的检查):

from typing import Any

def parse_json(text: str) -> Any:
    import json
    return json.loads(text)

类的类型标注

类可以用作类型,标注方法与属性。

class User:
    name: str
    age: int

    def __init__(self, name: str, age: int) -> None:
        self.name = name
        self.age = age

    def birthday(self) -> None:
        self.age += 1

注意name: str 写在类体里只是类型提示,并不会真正定义实例属性,还是需要 init 里用 self.name = name 来创建。不过,这个提示对静态检查器和 IDE 很有用。

当方法需要返回当前类的实例时,可以用 -> "ClassName" 的字符串形式(因为类定义尚未完成):

class Node:
    def set_next(self, node: "Node") -> None:
        ...
    def clone(self) -> "Node":
        ...

Python 3.11+ 可以使用 typing.Self 来表示返回当前类(更推荐):

from typing import Self

class Node:
    def clone(self) -> Self:
        return self.__class__()

泛型类:如果你想写一个能容纳任意类型的容器,可以用 TypeVarGeneric

from typing import TypeVar, Generic

T = TypeVar('T')

class Stack(Generic[T]):
    def __init__(self) -> None:
        self._items: list[T] = []

    def push(self, item: T) -> None:
        self._items.append(item)

    def pop(self) -> T:
        return self._items.pop()

使用时标注具体类型:

int_stack: Stack[int] = Stack()

常用工具与最佳实践

  1. 静态类型检查器
  • mypy:最成熟,安装后运行 mypy your_script.py 即能报告类型错误。
  • pyright / pylance:VS Code 内置的语言服务器,提供实时检查反馈。
  • 二者都能与预提交钩子 (pre-commit) 和 CI 流程结合,在合并前拦截类型问题。
  1. 运行时类型无关

类型提示 不会 强制类型检查。以下代码完全合法,不会报错:

   x: int = "hello"  # 运行时不会报任何错误
   

要让运行时检查类型,需借助 pydanticdataclassesfield 验证或 typeguard 等库。

  1. 渐进式标注

不要追求一次性给整个项目加上完整类型。可以:

  • 优先标注公有 API 的函数签名。
  • 对复杂逻辑或容易出错的部分加上类型。
  • 使用 Any 作为临时“逃生口”,等理清逻辑后再细化。
  • 配置文件 mypy.ini 逐步收紧检查等级(从 disallow_untyped_defs=False 开始)。
  1. 类型别名

复杂的嵌套类型可以用别名提高可读性:

   Vector = list[float]
   UserDict = dict[str, str | int | None]
   
  1. 处理第三方库无类型提示
  • 生成或安装对应的 stub 包(如 requests 就有 types-requests)。
  • 对于极个别无法标注的调用,加 # type: ignore 注释来暂时屏蔽检查。
  1. 与数据类搭配

dataclasses 天生就需要类型提示:

   from dataclasses import dataclass

   @dataclass
   class Point:
       x: float
       y: float
   

这样既定义了字段,又自动生成了 init,同时保留了完整的类型信息,一举多得。

类型提示是 Python 工程化的关键拼图。它让大型项目的代码更可维护、重构更安全,也让你在使用现代 IDE 时享受到精准的自动补全和错误提示。不必等到项目完美时再开始,从下一个函数签名加一个 -> str 开始,你就会体验到它带来的踏实感。