2021 年 10 月发布的 Python 3.10,是近十年来"语法变化最大"的一个版本:PEP 634 引入了 C 系语言里常见的 match 语句,让"按结构分发"成为一等公民;PEP 604 带来联合类型,int | str 从此可以写进类型标注。如果说 3.9 是修修补补,3.10 就是大刀阔斧——match 让一串串 if/elif 有了更优雅的替代品,联合类型则让类型提示终于像人话。

1. match 语句:结构模式匹配

match 不是简单的 switch。它匹配的是"结构"而非数值:字面量、变量捕获、序列、映射、类实例都可以作为模式,还能附加守卫条件。看一个把 HTTP 状态码翻译成人类语言的例子:

def describe(status):
    match status:
        case 200:
            return "OK"
        case 404:
            return "未找到"
        case int(n) if n >= 500:
            return f"服务器错误 {n}"
        case int(n):
            return f"其它状态码 {n}"
        case str(s):
            return f"状态字符串 {s!r}"
        case _:
            return "无法识别的状态"

几个要点:模式从上往下逐个尝试,命中即停,所以 case 200 必须写在 case int(n) 之前;类模式 int(n) 匹配成功会把值绑定到变量 n;守卫 if n >= 500 在匹配后追加条件;case _ 是通配符,兜底一切。常用模式见下表:

那 match 和 if/elif 怎么选?经验法则是:分支条件涉及"结构判断"(类型、形状、属性组合)时用 match,只是简单比较数值或布尔时 if 更直白。match 最大的价值是把"解构 + 分发"合并成一步,代码读起来像一张声明式的规则表。

模式示例说明
字面量case 404精确匹配某个值
通配符case _匹配任意值,不绑定
捕获case [x, y]匹配序列并绑定变量
OR 模式case 1 | 2多个可选值
类模式case Point(x=0, y=0)按属性匹配并解构
守卫case int(n) if n > 0匹配后附加条件

2. 用类模式解构对象

类模式按属性匹配并绑定,配合 dataclass 使用尤其舒服——解构逻辑直接写在匹配里,比一连串 isinstance 加属性判断清爽得多:

from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int

def locate(p):
    match p:
        case Point(x=0, y=0):
            return "原点"
        case Point(x=0, y=y):
            return f"在 y 轴上,y={y}"
        case Point(x=x, y=y):
            return f"普通点 ({x}, {y})"
        case _:
            return "不是 Point 对象"

print(locate(Point(0, 0)))    # 原点
print(locate(Point(0, 5)))    # 在 y 轴上,y=5
print(locate(Point(3, 4)))    # 普通点 (3, 4)

类模式还支持按位置匹配:case Point(0, 0) 等价于 case Point(x=0, y=0),字段多时建议用关键字形式,可读性更好。另外 match 匹配的是属性值而非内存地址,两个内容相同但不同的对象也能命中,这和 == 的语义一致。

3. 序列模式与映射模式

除了标量,模式还能直接拆解序列和字典。解析命令行参数、拆分坐标对、校验配置结构,都是它们的用武之地:

def parse_command(args):
    match args:
        case ["quit"]:
            return "退出"
        case ["add", x, y]:
            return f"加法: {int(x) + int(y)}"
        case ["serve", port] if port.isdigit():
            return f"在端口 {port} 启动服务"
        case [name, *rest]:
            return f"{name},还有参数 {rest}"
        case _:
            return "无法识别"

print(parse_command(["add", "3", "4"]))    # 加法: 7
print(parse_command(["serve", "8080"]))    # 在端口 8080 启动服务

["add", x, y] 是序列模式,要求列表恰好三个元素并依次绑定变量;[name, *rest] 用星号收集剩余元素,和解包语法一脉相承。映射模式则写成 case {"code": c} 的形式按键匹配字典,解析 JSON 结构时非常顺手。

4. 联合类型:int | str

PEP 604 让类型标注可以直接用 | 连接多个类型:Optional[X] 从此写成 X | None,Union[A, B] 写成 A | B。代码更短,而且无需从 typing 导入:

def parse_id(value: int | str) -> int:
    if isinstance(value, str):
        return int(value)
    return value

def find(key: str) -> dict | None:
    """返回字典,找不到时返回 None"""
    ...

更妙的是,isinstance 也支持联合类型,运行时检查与类型标注第一次用上了同一套语法:

def check(x):
    if isinstance(x, int | str):
        print("整数或字符串")
    else:
        print("其它类型")

5. zip(strict=True) 与括号化上下文管理器

zip 默认会静默截断较长的可迭代对象,数据错位时很难察觉。3.10 新增 strict=True 参数,长度不一致直接抛 ValueError,批量处理成对数据时强烈建议开启:

names = ["a", "b"]
scores = [90, 85, 95]

for n, s in zip(names, scores, strict=True):
    print(n, s)
# ValueError: zip() argument 2 is longer than argument 1

另外,3.10 允许用括号包住多个上下文管理器并换行书写,长的 with 语句不再需要反斜杠续行:

with (
    open("a.txt") as fa,
    open("b.txt") as fb,
):
    print(fa.read(), fb.read())

strict 模式最适合"两条数据必须一一对应"的场景,比如把日期列表与销量列表按行配对、把日志行号与时间戳对齐。开发阶段开着 strict,数据源一旦变化,程序立刻报错,而不是悄悄产出错位的结果。

6. 其它 3.10 更新速览

除了上面的重头戏,3.10 还有一批"小而美"的更新:

7. 如何安装 Python 3.10

pyenv install 3.10.16
pyenv global 3.10.16
python3.10 --version
python3.10 -m pip install --upgrade pip
python3.10 -m venv .venv && source .venv/bin/activate

8. 值不值得升级?

值得。3.10 是第一个让 match 走进日常的版本,联合类型更是被 3.11、3.12 一路继承的"基座语法",学会它等于为后续所有版本铺路。它目前仍在安全维护期内(官方支持到 2026 年 10 月),第三方库兼容性良好,从 3.8/3.9 升上来几乎没有破坏性改动。两个小提醒:一是 match 的模式语法细节多,序列模式、映射模式、OR 模式各有坑,初学建议先在脚本里练熟再大规模重构;二是如果你已经用 3.11+,完全不必回退,但读懂 match 依然是理解类型系统演进(如 PEP 695)的必修课。总体判断:作为生产版本,3.10 仍可用,但新项目优先选 3.11+;作为学习对象,它值得你完整过一遍。

一个容易踩的细节:模式匹配对类型敏感,case 1 不会匹配字符串 "1";调试时多打印 type(value) 确认运行时类型。联合类型虽然写着方便,但过度使用会让标注失去信息量,建议只在"确实可能是多种类型"的地方使用。

最后提醒一句时间线:3.10 的安全维护到 2026 年 10 月结束,如果你正在规划明年的版本升级,不如直接一步到位迁到 3.11 或 3.12;而 3.10 作为"语法分水岭",非常适合用来系统学习 match 与联合类型这两块基石。

💡 学习建议:拿手头一段冗长的 if/elif 链改写成 match,体会结构匹配的威力;再把代码里所有 Optional[X]Union[A, B] 换成 X | NoneA | B。两个动作做完,3.10 的核心就吃透了。