第2章 2.2 match-case 模式匹配(3.10+)
🎯 为什么你需要一个更聪明的「万能钥匙」?
上一章我们学了 if/elif/else,就像学会了用钥匙开门——但有时候你要开的是一扇有很多把锁的门,每把锁对应不同的钥匙。你有没有遇到过这种情况:
- 写了一个计算器,要根据运算符执行不同操作,加完减、减完乘……
- 做一个菜单系统,用户输入不同命令要执行不同功能
- 处理 API 返回的数据,不同状态码做不同处理
用 if/elif/else 写出来,代码越来越长,读起来像绕口令。有没有更清爽的写法?
学完这章,你能:
- 用 match-case 把一长串 if/elif/else 变成一目了然的结构
- 用「模式」精准匹配数据结构(列表、字典、元组)
- 给匹配加上「守卫」条件,做更精细的筛选
🧱 基础 25 分钟:match-case 是什么?
2.2.1 从「点菜」说起
想象你去餐厅点菜:
- 你说「红烧肉」,服务员给你端红烧肉
- 你说「糖醋排骨」,服务员给你端糖醋排骨
- 你说「随便」,服务员问你到底要什么
这就是 match-case 的工作方式——拿你的输入去匹配一个模式,匹配上了就执行对应的代码。
# 点菜模拟器
菜名 = "红烧肉"
match 菜名:
case "红烧肉":
print("给您来一份红烧肉,微甜口")
case "糖醋排骨":
print("糖醋排骨马上好")
case "随便":
print("那给您推荐我们的招牌菜")
case _:
print("不好意思,这个菜我们没有")
输出:
给您来一份红烧肉,微甜口
第 5 行 case _: 里的 _ 叫「通配符」,类似 else,前面都没匹配上就执行它。
2.2.2 为什么需要 match-case?
你可能想:「if/elif/else 不是也能做吗?」
举个例子,小明要写一个计算器,用 if/elif/else 这么写:
def 计算(运算符, a, b):
if 运算符 == "+":
return a + b
elif 运算符 == "-":
return a - b
elif 运算符 == "*":
return a * b
elif 运算符 == "/":
if b != 0:
return a / b
else:
return "除数不能为0"
else:
return "不支持的运算符"
这还好,但如果有 20 种运算符呢?代码越拉越长。
用 match-case 重写:
def 计算(运算符, a, b):
match 运算符:
case "+":
return a + b
case "-":
return a - b
case "*":
return a * b
case "/":
if b == 0:
return "除数不能为0"
return a / b
case _:
return "不支持的运算符"
说白了:match-case 让你写「当输入是 X 时做 Y」,比「如果输入是 X 否则如果输入是 Y」更符合人类思维。
2.2.3 匹配多个值(OR 逻辑)
有时候几种情况要执行相同的代码:
def 回应(指令):
match 指令:
case "你好" | "您好" | "嗨":
return "Hello!有什么可以帮你?"
case "谢谢" | "感谢":
return "不客气!"
case "再见" | "拜拜" | "溜了":
return "下次见!"
case _:
return "我不太懂你的意思"
print(回应("您好"))
print(回应("拜拜"))
输出:
Hello!有什么可以帮你?
下次见!
用 | 分隔,表示「任选其一匹配」。
2.2.4 用变量捕获值
上一章小明的购物清单里,每种商品有「名称」和「数量」。如果要根据不同商品做不同处理:
def 处理商品(商品):
match 商品:
case "口罩", 数量 if 数量 > 10:
return f"大量口罩订单:{数量}个,优先发货"
case "口罩", 数量:
return f"口罩订单:{数量}个"
case "电脑", _:
return "贵重物品,小心包装"
case _:
return "普通商品,常规处理"
print(处理商品(("口罩", 20))) # 大量口罩订单:20个,优先发货
print(处理商品(("口罩", 5))) # 口罩订单:5个
print(处理商品(("电脑", 1))) # 贵重物品,小心包装
注意这里的 case "口罩", 数量 if 数量 > 10:,用 if 加了个守卫条件——只有匹配模式且满足条件才执行。

2.2.5 匹配列表和元组
假设你拿到了一个坐标数据,要根据不同情况处理:
def 分析坐标(坐标):
match 坐标:
case [0, 0]:
return "原点"
case [x, 0]:
return f"X轴上的点,X={x}"
case [0, y]:
return f"Y轴上的点,Y={y}"
case [x, y] if x == y:
return f"在对角线上,X=Y={x}"
case [x, y]:
return f"普通点({x}, {y})"
case _:
return "无法识别"
print(分析坐标([0, 0])) # 原点
print(分析坐标([5, 0])) # X轴上的点,X=5
print(分析坐标([3, 3])) # 在对角线上,X=Y=3
[x, y] 模式会捕获匹配的值,后续代码里就能用 x 和 y。
2.2.6 匹配字典(关键字模式)
有时候 API 返回的是字典格式:
def 解析响应(响应):
match 响应:
case {"status": "success", "data": 数据}:
return f"成功!数据是:{数据}"
case {"status": "error", "message": 消息}:
return f"出错了:{消息}"
case {"status": "loading"}:
return "加载中,请稍候..."
case _:
return "未知响应格式"
api结果 = {"status": "success", "data": [1, 2, 3]}
print(解析响应(api结果))
{"status": "success", "data": 数据} 这种模式,只关心 status 和 data 字段,忽略其他字段(如果有的话)。
2.2.7 match-case vs if/elif/else
| 场景 | 推荐用 |
|---|---|
| 条件不多(2-3个),逻辑简单 | if/elif/else |
| 大量分支,每个分支简单 | match-case |
| 要匹配数据结构(列表、元组、字典) | match-case 碾压 |
| 需要复杂的组合条件 | if/elif/else 更灵活 |
一个经验:如果你发现 if/elif/else 写了超过 5 个分支,考虑换成 match-case。
🔥 实战 35 分钟:3 个小项目
项目 1:命令解析器(5 分钟)
做一个简单的命令行工具,解析用户输入的命令:
def 解析命令(命令):
"""模拟一个简单的命令解析器"""
parts = 命令.strip().split()
if not parts:
return "没有输入命令"
match parts:
case ["help"]:
return "可用命令:help, status, quit, echo [内容]"
case ["status"]:
return "系统状态:正常运行中"
case ["quit"] | ["exit"]:
return "再见!"
case ["echo", 内容]:
return f"你说了:{内容}"
case ["echo"]:
return "echo 需要一个参数"
case _:
return f"未知命令:{parts[0]},输入 help 查看帮助"
# 测试
print(解析命令("help"))
print(解析命令("status"))
print(解析命令("echo 你好"))
print(解析命令("quit"))
print(解析命令("随便打点啥"))
输出:
可用命令:help, status, quit, echo [内容]
系统状态:正常运行中
你说了:你好
再见!
未知命令:随便打点啥,输入 help 查看帮助
这个例子展示了:match-case 如何优雅地处理带参数的命令,比一堆 if first_word == "help" 清晰多了。
项目 2:JSON 数据分类器(15 分钟)
从 CSV 文件读取学生成绩,根据评级分类输出:
import csv
from io import StringIO
# 模拟的 CSV 数据(实际可以从文件读取)
csv数据 = """姓名,数学,语文,英语
小明,95,88,92
小红,67,72,65
小刚,45,55,48
小美,88,91,89
大力,33,40,28"""
def 获取评级(平均分):
"""根据平均分返回评级"""
match 平均分:
case n if n >= 90:
return "A"
case n if n >= 80:
return "B"
case n if n >= 70:
return "C"
case n if n >= 60:
return "D"
case _:
return "F"
def 处理成绩数据(csv文本):
"""解析 CSV 并分类学生"""
结果 = []
reader = csv.DictReader(StringIO(csv文本))
match list(reader):
case []:
return "没有数据"
reader = csv.DictReader(StringIO(csv文本))
for 行 in reader:
姓名 = 行["姓名"]
数学 = int(行["数学"])
语文 = int(行["语文"])
英语 = int(行["英语"])
平均分 = (数学 + 语文 + 英语) / 3
评级 = 获取评级(平均分)
match (评级, 平均分):
case ("A", _):
建议 = "继续保持!"
case ("F", _):
建议 = "需要补课了"
case (_, n) if n >= 60:
建议 = "稳扎稳打"
case _:
建议 = "加油!"
结果.append(f"{姓名}:平均分{平均分:.1f},评级{评级},{建议}")
return "\n".join(结果)
print(处理成绩数据(csv数据))
输出:
小明:平均分91.7,评级A,继续保持!
小红:平均分68.0,评级D,加油!
小刚:平均分49.3,评级F,需要补课了
小美:平均分89.3,评级B,稳扎稳打
大力:平均分33.7,评级F,需要补课了
这个例子展示了:match-case 配合 if 守卫条件,如何实现复杂的分类逻辑。

项目 3:小明的购物清单增强版(15 分钟)
结合前两章学的内容,做一个实用的购物清单管理工具:
from datetime import datetime
# 购物清单数据结构
购物清单 = []
def 添加商品(名称, 数量=1, 类别="其他"):
"""添加商品到清单"""
match (名称, 数量, 类别):
case ("口罩", n, "医疗") if n > 50:
print(f"⚠️ 大量口罩订单({n}个),已优先添加")
case ("电脑", _, "电子产品"):
print("💻 电子产品已添加,注意保修")
购物清单.append({
"名称": 名称,
"数量": 数量,
"类别": 类别,
"时间": datetime.now().strftime("%H:%M")
})
return f"已添加 {数量} 个 {名称}"
def 显示清单():
"""按类别显示清单"""
if not 购物清单:
return "清单是空的"
分类汇总 = {}
for 商品 in 购物清单:
类别 = 商品["类别"]
if 类别 not in 分类汇总:
分类汇总[类别] = []
分类汇总[类别].append(商品)
match 分类汇总:
case {"生鲜": 生鲜列表, **其他} if 生鲜列表:
print(f"🦐 生鲜商品有 {len(生鲜列表)} 项,需要保鲜!")
case _:
pass
输出 = ["=" * 30, "🛒 购物清单", "=" * 30]
for 类别, 商品列表 in 分类汇总.items():
输出.append(f"\n📦 {类别}:")
for p in 商品列表:
输出.append(f" - {p['名称']} x {p['数量']} ({p['时间']})")
输出.append("=" * 30)
总数 = sum(p["数量"] for p in 购物清单)
输出.append(f"📊 总计 {总数} 件商品")
return "\n".join(输出)
def 删除商品(名称):
"""删除商品"""
global 购物清单
原长度 = len(购物清单)
购物清单 = [p for p in 购物清单 if p["名称"] != 名称]
match len(购物清单), 原长度:
case n, m if n < m:
return f"删除了 {m - n} 个 {名称}"
case _:
return f"清单里没有 {名称}"
# 操作演示
print(添加商品("口罩", 20, "医疗"))
print(添加商品("口罩", 100, "医疗"))
print(添加商品("鸡蛋", 12, "生鲜"))
print(添加商品("牛奶", 2, "生鲜"))
print(添加商品("电脑", 1, "电子产品"))
print()
print(显示清单())
print()
print(删除商品("鸡蛋"))
print(删除商品("不存在的东西"))
输出:
已添加 20 个 口罩
⚠️ 大量口罩订单(100个),已优先添加
已添加 100 个 口罩
已添加 12 个 鸡蛋
🦐 生鲜商品有 2 项,需要保鲜!
已添加 2 个 牛奶
💻 电子产品已添加,注意保修
已添加 1 个 电脑
==============================
🛒 购物清单
==============================
📦 医疗:
口罩 x 20 (14:30)
口罩 x 100 (14:30)
📦 生鲜:
鸡蛋 x 12 (14:30)
牛奶 x 2 (14:30)
📦 电子产品:
电脑 x 1 (14:30)
==============================
📊 总计 134 件商品
删除了 1 个 鸡蛋
清单里没有 不存在的东西
这个例子展示了:用 match-case 处理业务逻辑(批量口罩提醒、生鲜保鲜提醒),代码读起来像「规格说明书」。
💪 进阶 20 分钟:常见坑 + 小贴士
坑 1:忘记加通配符
# ❌ 错误:没有处理其他情况
match 指令:
case "start":
return "启动"
case "stop":
return "停止"
# ✅ 正确:加上默认处理
match 指令:
case "start":
return "启动"
case "stop":
return "停止"
case _:
return "未知指令"
坑 2:模式里用了未定义的变量
# ❌ 错误:只想匹配长度为2的列表,但忘了 list 关键字
match 数据:
case [x, y]: # 如果是列表比较,这个模式也会匹配其他长度的列表
return x + y
# ✅ 正确:用多少元素就匹配多少
match 数据:
case [x, y] if len(数据) == 2:
return x + y
case _:
return "数据格式不对"
坑 3:守卫条件写在错误的位置
# ❌ 错误:守卫写在最前面,逻辑不清
match 数据:
case n if n > 0 "正数": # 这样写语法错误
pass
# ✅ 正确:守卫跟在模式后面
match 数据:
case n if n > 0:
return "正数"
case n if n < 0:
return "负数"
case 0:
return "零"
坑 4:列表模式和元组模式混用
坐标 = (10, 20)
# ❌ 错误:混用
match 坐标:
case [x, y]: # 列表模式配元组,匹配失败
return x + y
# ✅ 正确:元组配元组,列表配列表
match 坐标:
case (x, y):
return x + y
坑 5:过度使用 match-case
# ❌ 错误:简单判断没必要用
match x:
case 1:
return True
case _:
return False
# ✅ 正确:简单布尔用 if
return x == 1
性能小贴士
match-case 在 Python 3.11+ 有显著性能优化。如果你在做一个高性能服务,确保用 Python 3.11 以上。3.10 版本的 match 会有一点 overhead。
# Python 3.11+ 特性:可以这样用
match 数据:
case {"users": [*用户列表]} if len(用户列表) > 0: # 3.11+ 星号模式更强大
处理用户(用户列表)
调试技巧
match-case 里调试,最好的方式是加 print 中间变量:
def 调试示例(数据):
print(f"调试:收到数据 = {数据}") # 先打印看输入
match 数据:
case [x, y]:
print(f"匹配到两点:{x}, {y}")
return x + y
case _:
print("没有匹配")
return None
✏️ 练习题
练习 1(2 分钟):改改计算器
- 输入:
("*", 5, 3) - 预期输出:
15 - 提示:把「项目 1」里的计算函数拿来,只改运算符
练习 2(2 分钟):加个新运算符
- 输入:
("**", 2, 3) - 预期输出:
8(2 的 3 次方) - 提示:在
case _:前面加一行case "**":
练习 3(3 分钟):处理新数据
- 输入:
{"name": "小刚", "score": 55} - 预期输出:
"小刚需要加油,当前分数:55" - 提示:用项目 2 的评级逻辑,但数据是字典格式
练习 4(3 分钟):串起来
- 用
match-case写一个函数,输入是(命令, 数据)元组 "filter"命令:过滤数据里分数大于 60 的项"group"命令:按评级分组"sort"命令:按分数从高到低排序- 提示:组合项目 2 的逻辑
练习 5(2 分钟):找错误
- 下面代码运行会输出什么?为什么?
def test(data):
match data:
case [x, y] if x > y:
return "x比较大"
case [x, y]:
return "y比较大"
print(test([3, 5]))
print(test([10, 2]))
print(test([5, 5]))
- 预期输出:应该是?
- 提示:第三个
5, 5匹配哪个 case?
作业:做一个「命令式购物车」
做一个命令行购物车程序,支持以下命令:
基本功能(必须):
1. add [商品] [数量] - 添加商品
2. remove [商品] - 删除商品
3. list - 显示购物车
4. total - 显示总数量
5. clear - 清空购物车
进阶功能(选做):
- category [商品] [类别] - 给商品标记类别
- 相同商品累加数量
- 根据类别分组显示
验收标准:
- 能跑起来
- 所有命令都有响应
- 用 match-case 处理所有命令解析
- 加上注释说明每个 case 在干嘛
加分项:
- 实现 undo 撤销功能
- 实现 save/load 保存到文件
📚 总结
本文学到的 3 件事:
1. match-case 是 Python 3.10+ 的「结构化条件匹配」,比一堆 if/elif/else 更清晰
2. 可以匹配字面值、变量、列表、元组、字典,还能加 if 守卫条件
3. 用 | 可以组合多个匹配值,用 _ 做通配符
延伸资源:
- 官方文档:PEP 636 – Structural Pattern Matching
- 视频:搜索 "Python 3.10 match case tutorial" 有很多实战演示
- 练习:去 LeetCode 刷几道用 pattern matching 的题
互动钩子:
你在实际项目里有没有遇到过「一堆 if/elif/else 写得自己都晕了」的情况?后来怎么解决的?评论区聊聊,选 3 个老粉问题我亲自回答!
📢 下章预告:学会了条件分支,你可能会想「能不能让代码自动重复执行?」——下一章「第 2.3 while 循环与死循环」,教你让计算机帮你做重复劳动,同时也会告诉你什么情况下循环会「卡死」以及如何避免!

评论(0)