返回专题首页

Python 专题

给动态语言加一点约束:Python 类型标注入门

Python 是动态语言,但这并不意味着它不需要类型信息。随着项目规模增长,参数含义、返回值结构、可空性边界都会变得越来越重要,而类型标注正是帮助我们降低沟通成本和理解成本的重要手段。

Python 专题第 17 篇 / 39 篇4 分钟

Python 是动态语言,但这并不意味着它不需要类型信息。随着项目规模增长,参数含义、返回值结构、可空性边界都会变得越来越重要,而类型标注正是帮助我们降低沟通成本和理解成本的重要手段。

这一节我们会先从最基础的标注方式开始,解释它能解决什么问题、不能解决什么问题,再把它和编辑器提示、静态检查工具之间的关系理清楚。你会看到,类型标注并不是“让 Python 变成别的语言”,而是让动态代码更可维护。

为什么 Python 也需要类型标注?

类型标注首先解决的,不是运行时限制,而是协作和理解成本。

例如:

def format_user(data):
    ...

光看这个签名,你很难知道:

  • data 是字典、对象还是字符串?
  • 返回值是什么结构?
  • 允许不允许传 None

而加上标注后:

def format_user(data: dict[str, str]) -> str:
    ...

函数意图就清楚得多了。

所以类型标注的第一价值,是帮助阅读者快速建立心智;第二价值,是让静态工具提前发现一些低级错误;第三价值,才是慢慢推动设计变得更清楚。

基础类型标注写法

变量与函数标注

最常见的写法,是给变量、参数和返回值补充类型信息:

name: str = "Colin"


def greet(name: str) -> str:
    return f"Hello, {name}"

这类标注本身不会改变 Python 的动态运行方式,但会让编辑器、类型检查器和阅读者拥有更清晰的上下文。

容器类型标注

当你处理列表、字典、集合时,通常更重要的是“里面装的是什么”:

names: list[str] = ["Colin", "Alice"]
scores: dict[str, int] = {"math": 95}
tags: set[str] = {"python", "backend"}

容器类型一旦写清楚,很多函数签名和数据流都会更容易理解。

这也是为什么类型标注的重点往往不在基础类型本身,而在结构关系上。

常见类型工具:OptionalUnionLiteral

现实代码里,数据并不总是单一类型。

比如某个值可能为 None,就常写成:

from typing import Optional

nickname: Optional[str] = None

本质上它等价于“str | None”。从较新的 Python 语法视角看,后者会更直接。

如果一个值可能有多个类型,可以用 Union|

value: int | str

Literal 则适合表达有限个固定取值:

from typing import Literal

status: Literal["draft", "published", "archived"]

它的价值在于,很多时候你真正想表达的不是“它是字符串”,而是“它只能是这几个特定字符串之一”。

所以,类型工具真正带来的提升,是让类型从“粗粒度种类”进一步走向“更贴近业务约束的描述”。

类型标注不是运行时校验

这是初学类型标注时最容易混淆的一点。

Python 写了类型标注,并不意味着运行时会自动帮你拦截错误输入。比如:

def greet(name: str) -> str:
    return f"Hello, {name}"

你在运行时依然可能传入整数,而 Python 默认不会因为标注就直接报错。

所以一定要把两件事分开:

  • 类型标注主要服务于静态分析、编辑器提示和代码沟通;
  • 运行时校验需要依赖显式判断或框架能力,比如 Pydantic 之类的工具。

只要这条边界不混淆,你就不会对类型标注产生不切实际的期待。

mypy、pyright 与编辑器提示的关系

类型标注真正发挥作用,通常离不开工具链。

最常见的两个静态检查工具,是 mypypyright。它们会读取你的类型标注,帮你发现诸如:

  • 返回值类型不一致;
  • 可空值没处理;
  • 容器元素类型不匹配;
  • 调用参数类型错误。

而编辑器补全和悬浮提示,通常也是基于这些类型信息工作的。

所以类型标注并不是“写给 Python 解释器看”的,而更像是写给:

  • 你未来的自己;
  • 你的同事;
  • 你的编辑器和静态分析工具。

也正因为如此,类型标注的收益通常会随着项目规模增大而越来越明显。

总结与预告

这一节我们建立了对 Python 类型标注的第一层认知:它不是为了把 Python 变“死”,而是为了让项目在规模增长之后仍然能保持沟通清晰和改动可控。类型标注的价值,更多体现在协作、提示和维护上。

下一节我们会继续进入更进阶的类型系统能力,讨论泛型、TypeVarProtocol 等工具究竟如何落到真实工程里。