飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • 第1章 Python简介

    • Python是什么
    • 安装与运行
    • 交互式解释器
    • 注释与编码规范
  • 第2章 变量与数据类型

    • 变量与对象
    • 整数 int
    • 浮点数 float
    • 复数 complex
    • 布尔值 bool
    • 字符串 str
    • 空值 None
    • 类型转换
  • 第3章 运算符与表达式

    • 算术运算符
    • 比较运算符
    • 赋值运算符
    • 逻辑运算符
    • 位运算符
    • 身份与成员运算符
    • 海象运算符
    • 运算符优先级
  • 第4章 流程控制

    • if 语句
    • if-else 语句
    • if-elif-else 语句
    • match-case 语句
    • 条件表达式(三元运算符)
    • while 循环
    • for 循环
    • range 函数
    • break 与 continue
    • 循环的 else 子句
    • pass 语句
  • 第5章 数据结构

    • 列表创建与索引
    • 列表方法
    • 列表推导式
    • 元组
    • 序列解包
    • 集合
    • 字典创建与访问
    • 字典方法
    • 字典推导式
    • range 对象
  • 第6章 函数

    • 定义函数
    • 位置参数与关键字参数
    • 默认参数
    • 可变参数
    • 解包实参
    • 函数返回值
    • lambda 表达式
    • 文档字符串与注解
    • 作用域与命名空间
    • global 与 nonlocal
  • 第7章 模块与包

    • 模块导入
    • 模块搜索路径
    • 包与相对导入
    • 标准库概览
  • 第8章 文件与输入输出

    • 文件读写
    • 上下文管理器
    • 字符串格式化
    • JSON 与 CSV
  • 第9章 面向对象

    • 类与对象
    • 方法
    • 实例变量与类变量
    • 私有变量
    • 继承
    • 多重继承
    • 魔术方法
    • 属性装饰器
    • 数据类 dataclass
  • 第10章 异常处理

    • 语法错误与异常
    • try-except
    • 异常链与 raise
    • 清理操作
    • 自定义异常
  • 第11章 迭代器与生成器

    • 迭代器协议
    • 生成器
    • 生成器表达式
    • 迭代工具
  • 第12章 高级特性

    • 装饰器
    • 函数式编程
  • 第13章 工程实践

    • 测试与调试
    • 代码质量
    • 虚拟环境

match-case 语句

match-case 是 Python 3.10 引入的结构模式匹配语句。它表面上像 C、Java 中的 switch,但功能更接近 Rust 或 Haskell 的模式匹配——不仅能比较值,还能解构数据结构、绑定变量,并根据值的"形状"选择执行路径。

基本语法

match 接受一个表达式(称为主语),将其值与一系列 case 模式依次比较,只有第一个匹配的模式会被执行:

def http_error(status):
    match status:
        case 400:
            return "Bad request"
        case 404:
            return "Not found"
        case 418:
            return "I'm a teapot"
        case _:
            return "Something's wrong with the internet"

print(http_error(404))  # Not found

最后一个 case _ 中的 _ 是通配符,它匹配任何值,相当于 switch 中的 default。如果没有通配符且所有 case 都不匹配,程序直接跳过整个 match 语句,不执行任何分支。

字面值模式与或模式

最简单的模式是字面值模式,将主语与常量直接比较:

dept = "技术部"

match dept:
    case "技术部":
        print("负责产品研发")
    case "产品部":
        print("负责需求设计")
    case "运营部":
        print("负责用户增长")
    case _:
        print("未知部门")

可以用 | 在一个 case 中组合多个字面值,实现"或"逻辑:

match status:
    case 401 | 403 | 404:
        return "Not allowed"

这相当于 if status in (401, 403, 404),但写法更紧凑。

捕获模式

模式可以包含变量,将主语中的值绑定到变量名。这种能力让 match 远超简单的等值比较:

point = (0, 5)

match point:
    case (0, 0):
        print("原点")
    case (0, y):
        print(f"Y 轴上,Y={y}")
    case (x, 0):
        print(f"X 轴上,X={x}")
    case (x, y):
        print(f"X={x}, Y={y}")
    case _:
        raise ValueError("不是有效的点")

当 point 为 (0, 5) 时,(0, y) 模式匹配成功,变量 y 被绑定为 5。(x, y) 虽然也能匹配,但由于 case 按顺序执行且只匹配第一个,不会到达该分支。

捕获模式与解包赋值 (x, y) = point 概念类似,但增加了字面值约束——case (0, y) 要求第一个元素必须是 0。

类模式与属性捕获

如果数据用类组织,可以用"类名 + 参数列表"的形式匹配并提取属性:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

p = Point(0, 5)

match p:
    case Point(x=0, y=0):
        print("原点")
    case Point(x=0, y=y):
        print(f"Y 轴上,Y={y}")
    case Point(x=x, y=0):
        print(f"X 轴上,X={x}")
    case Point():
        print("平面上某处")
    case _:
        print("不是 Point")

Point() 匹配任何 Point 实例,无论属性值如何。通过设置类的 __match_args__ 属性,还可以使用位置参数:

class Point:
    __match_args__ = ('x', 'y')
    def __init__(self, x, y):
        self.x = x
        self.y = y

match p:
    case Point(0, 0):
        print("原点")
    case Point(0, y):
        print(f"Y={y}")

此时 Point(0, y)、Point(0, y=y)、Point(x=0, y=y) 是等价的。

序列模式与扩展解包

序列模式可以匹配列表、元组等序列,并支持扩展解包:

match points:
    case []:
        print("没有点")
    case [Point(0, 0)]:
        print("只有一个原点")
    case [Point(x, y)]:
        print(f"单点 ({x}, {y})")
    case [Point(0, y1), Point(0, y2)]:
        print(f"Y 轴上两点:{y1}, {y2}")
    case [first, *rest]:
        print(f"第一个:{first},其余:{rest}")
    case _:
        print("其他情况")

*rest 捕获剩余元素,类似于解包赋值。如果不需要绑定剩余元素,可以用 *_:

case [first, *_]:
    print(f"至少有一个元素,第一个是 {first}")

注意序列模式不能匹配迭代器或字符串,只能匹配序列类型(列表、元组等)。

映射模式

映射模式用于匹配字典,捕获指定键的值:

config = {"bandwidth": 100, "latency": 20, "region": "ap-east"}

match config:
    case {"bandwidth": b, "latency": l}:
        print(f"带宽:{b},延迟:{l}")
    case {"bandwidth": b}:
        print(f"仅带宽:{b}")
    case {}:
        print("空配置")
    case _:
        print("非字典")

映射模式遵循"子集匹配"原则:主语字典可以包含额外的键,只要指定的键存在即可。这与序列模式的"精确匹配"不同。**rest 解包也受支持,但 **_ 是冗余的,因此不允许使用。

守卫子句

向模式添加 if 条件,可以进一步约束匹配结果,这种 if 子句称为守卫子句(guard):

match point:
    case Point(x, y) if x == y:
        print(f"对角线上,坐标 {x}")
    case Point(x, y):
        print(f"不在对角线上")

守卫子句在模式匹配成功后求值,如果为假,则 match 继续尝试下一个 case。注意值的捕获发生在守卫求值之前,因此守卫中可以使用已绑定的变量。

与 if-elif 的对比

match-case 和 if-elif 都能处理多分支,但适用场景不同:

  • if-elif:条件可以是任意布尔表达式,适合复杂逻辑判断、范围比较
  • match-case:专注于值的"形状"和结构,适合等值比较、数据解构、类型分发
# if-elif 更适合范围判断
if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"

# match-case 更适合等值和结构判断
match status:
    case 200 | 201:
        return "成功"
    case 404:
        return "未找到"

官方文档指出:"如果是把一个值与多个常量进行比较,或者检查特定类型或属性,match 语句更有用。"

具名常量

模式可以使用具名常量(如枚举成员),但必须以带点号的名称出现,防止被解释为捕获变量:

from enum import Enum

class Color(Enum):
    RED = 'red'
    GREEN = 'green'
    BLUE = 'blue'

color = Color.RED

match color:
    case Color.RED:
        print("红色")
    case Color.GREEN:
        print("绿色")
    case Color.BLUE:
        print("蓝色")

如果写成 case RED:,RED 会被当作新变量绑定,而非引用 Color.RED。

常见错误

忘记通配符导致静默跳过:

match x:
    case 1:
        print("一")
    case 2:
        print("二")
# x=3 时什么都不发生,没有报错

混淆捕获变量与常量:

RED = 1

match x:
    case RED:  # 错误:RED 被视为新变量,总是匹配
        print("红色")

常量应通过模块/类限定(如 Status.RED)来避免被捕获。

match-case 为 Python 带来了声明式的数据匹配能力,在处理复杂数据结构、状态机、AST 遍历等场景时,代码比等价的 if-elif 链更清晰、更不易出错。

上一页
if-elif-else 语句
下一页
条件表达式(三元运算符)