查看: 555|回复: 3

Python 3.10结构化模式匹配match-case基础与实战

[复制链接]
发表于 前天 09:00 | 显示全部楼层 |阅读模式
2021年10月发布的Python 3.10带来了结构化模式匹配(Structural Pattern Matching),即match-case语句。它的能力远超传统switch,不仅能做值匹配,还能解构序列、映射和类实例,并支持守卫条件与变量绑定。本教程从基础语法到实战案例,系统讲解match-case的使用方法。

所有代码示例需在Python 3.10及以上版本运行,可用以下代码检查环境兼容性:
  1. import sys
  2. if sys.version_info >= (3, 10):
  3.     print("✅ 支持 match-case")
  4. else:
  5.     print("❌ 请升级到 Python 3.10 或更高版本")
复制代码

一、基础语法

1. 最简单的模式匹配

match语句后接待匹配值,每个case跟一个模式和一个冒号,形如:
  1. def what_day(day):
  2.     match day:
  3.         case 1:
  4.             return "星期一"
  5.         case 2:
  6.             return "星期二"
  7.         case 7:
  8.             return "星期日"
  9.         case _:
  10.             return "无效的日期数字"
复制代码

下划线_是通配符,匹配任何未在上方明确匹配的值。注意_不会绑定值,而普通变量名会绑定。

2. OR模式匹配多个值

用竖线|分隔多个值,实现“或”逻辑:
  1. def classify_number(n):
  2.     match n:
  3.         case 0:
  4.             return "零"
  5.         case 1 | 2 | 3:
  6.             return "小数字(1-3)"
  7.         case 4 | 5 | 6:
  8.             return "中数字(4-6)"
  9.         case 7 | 8 | 9:
  10.             return "大数字(7-9)"
  11.         case _:
  12.             return "超出范围"
复制代码

3. 变量绑定

这是match-case超越switch的关键:在匹配序列或结构时,模式中的变量会自动捕获对应的元素。
  1. def process_command(command):
  2.     match command.split():
  3.         case ["quit"]:
  4.             print("退出程序")
  5.         case ["add", x]:
  6.             print(f"加上 {x}")
  7.         case ["add", x, y]:
  8.             print(f"{x} + {y} = {int(x) + int(y)}")
  9.         case _:
  10.             print("未知命令")
复制代码

二、结构化模式匹配(核心特性)

1. 匹配序列(列表/元组)

支持按长度、位置以及使用星号收集剩余元素:
  1. def analyze_sequence(seq):
  2.     match seq:
  3.         case []:
  4.             return "空序列"
  5.         case [x]:
  6.             return f"单元素: {x}"
  7.         case [x, y]:
  8.             return f"两元素: {x}, {y}"
  9.         case [first, *rest]:
  10.             return f"首元素: {first}, 剩余: {rest}"
复制代码

星号也可出现在中间,例如[first, *middle, last]会分离首、尾及中间剩余元素。

2. 匹配映射(字典)

按字典键进行模式匹配,变量绑定对应值。注意case顺序影响匹配结果:更具体的模式应写在前面。
  1. def analyze_config_fixed(config):
  2.     match config:
  3.         case {"host": host, "port": port, "debug": bool(debug)}:
  4.             return f"服务器: {host}:{port}, 调试={'开' if debug else '关'}"
  5.         case {"host": host, "port": int(port)}:
  6.             return f"服务器: {host}:{port}"
  7.         case _:
  8.             return "无效配置"
复制代码

这里利用内置类型名(如bool, int)进行类型守卫。在模式中直接写int(port)会将port与int类型匹配,同时转换类型。

3. 匹配类实例

模式中可以调用构造函数并进行属性解构:
  1. class Point:
  2.     def __init__(self, x, y):
  3.         self.x = x
  4.         self.y = y
  5. def describe_shape(shape):
  6.     match shape:
  7.         case Point(x=0, y=0):
  8.             return "原点"
  9.         case Point(x=x, y=y):
  10.             return f"点({x}, {y})"
  11.         case _:
  12.             return "未知形状"
复制代码

三、守卫条件(Guard)

在模式后添加if条件,做额外逻辑判断:
  1. def classify_value(x):
  2.     match x:
  3.         case int(n) if n > 0:
  4.             return f"正整数: {n}"
  5.         case int(n) if n < 0:
  6.             return f"负整数: {n}"
  7.         case int(n):
  8.             return "零"
  9.         case str(s) if len(s) > 10:
  10.             return f"长字符串({len(s)}字符): {s[:10]}..."
  11.         case str(s):
  12.             return f"短字符串: {s}"
  13.         case _:
  14.             return "其他类型"
复制代码

守卫条件可结合OR模式,但注意优先级:case模式 if条件中的|需要括号(如case int(n) if n < 0 | n > 150: 实际应写作 case int(n) if n < 0 or n > 150:,注意原文的|用法有误,正确写法是条件表达式内用or)。

四、match-case vs if-elif-else

match-case适合值匹配和结构解构;if-elif-else适合范围比较、复杂逻辑。两者可混合使用,例如先用match按类型分支,再在case内部用if细分:
  1. def smart_classifier(obj):
  2.     match obj:
  3.         case int(n):
  4.             if n < 0:
  5.                 return "负整数"
  6.             elif n == 0:
  7.                 return "零"
  8.             else:
  9.                 return "正整数"
  10.         case str(s):
  11.             if len(s) == 0:
  12.                 return "空字符串"
  13.             else:
  14.                 return "非空字符串"
  15.         case _:
  16.             return "其他类型"
复制代码

五、实战案例

1. JSON数据验证与解析

利用模式匹配快速验证并提取字段:
  1. def parse_user_data(data):
  2.     match data:
  3.         case {
  4.             "name": str(name),
  5.             "age": int(age),
  6.             "email": str(email),
  7.             **extra
  8.         } if 0 < age < 150 and "@" in email:
  9.             return {"name": name, "age": age, "email": email, "valid": True}
  10.         case {"name": str(name), "age": int(age)} if 0 < age < 150:
  11.             return {"name": name, "age": age, "valid": True, "warnings": ["缺少邮箱"]}
  12.         case dict():
  13.             return {"valid": False, "errors": ["数据格式不正确"]}
  14.         case _:
  15.             return {"valid": False, "errors": ["输入不是字典"]}
复制代码

2. 简易计算器

用match-case处理命令解析和操作符分发:
  1. def calculator():
  2.     print("简易计算器(输入 quit 退出)")
  3.     while True:
  4.         user_input = input("> ").strip()
  5.         if not user_input:
  6.             continue
  7.         match user_input.split():
  8.             case ["quit" | "exit" | "q"]:
  9.                 print("再见!")
  10.                 break
  11.             case [left, op, right]:
  12.                 try:
  13.                     a, b = float(left), float(right)
  14.                     match op:
  15.                         case "+":
  16.                             result = a + b
  17.                         case "-":
  18.                             result = a - b
  19.                         case "*" | "×":
  20.                             result = a * b
  21.                         case "/" | "÷":
  22.                             if b == 0:
  23.                                 print("除数不能为零")
  24.                                 continue
  25.                             result = a / b
  26.                         case "**" | "^":
  27.                             result = a ** b
  28.                         case "%":
  29.                             result = a % b
  30.                         case _:
  31.                             print("未知操作符")
  32.                             continue
  33.                     print(f"{a} {op} {b} = {result}")
  34.                 except ValueError:
  35.                     print("请输入有效数字")
  36.             case _:
  37.                 print("格式错误,请使用: <数字> <操作符> <数字>")
复制代码

3. 抽象语法树处理器

利用match-case递归求值表达式树:
  1. class Number:
  2.     def __init__(self, value):
  3.         self.value = value
  4. class BinOp:
  5.     def __init__(self, left, op, right):
  6.         self.left = left
  7.         self.op = op
  8.         self.right = right
  9. class UnaryOp:
  10.     def __init__(self, op, operand):
  11.         self.op = op
  12.         self.operand = operand
  13. def evaluate(expr):
  14.     match expr:
  15.         case Number(value=n):
  16.             return n
  17.         case BinOp(left=l, op="+", right=r):
  18.             return evaluate(l) + evaluate(r)
  19.         case BinOp(left=l, op="*", right=r):
  20.             return evaluate(l) * evaluate(r)
  21.         case UnaryOp(op="-", operand=opd):
  22.             return -evaluate(opd)
  23.         case _:
  24.             raise ValueError("未知表达式")
复制代码

构建表达式(-5 + 3) * 2:
  1. expr = BinOp(BinOp(UnaryOp("-", Number(5)), "+", Number(3)), "*", Number(2))
  2. print(evaluate(expr))  # -4
复制代码

总结:match-case是Python 3.10的重大特性,其结构化模式匹配能力让代码更清晰、更易维护。掌握它,你就能用更声明式的方式处理分支逻辑。建议在需要解构复杂数据结构或多值分支时优先使用,并与if-elif-else合理搭配。
回复

使用道具 举报

发表于 前天 09:10 | 显示全部楼层

Re: Python 3.10结构化模式匹配match-case基础与实战

感谢楼主分享,非常系统!match-case 的序列解构和守卫条件真的比传统 switch 好用太多,尤其是 `[first, *rest]` 和 `case int(n) if n > 0` 这种写法,简洁又直观。还没在项目里用上 3.10 的新特性,看完教程打算升级试试了。顺便问一下,最后那个带守卫条件的 `classify_value` 例子好像没贴完整?
回复 支持 反对

使用道具 举报

发表于 前天 09:10 | 显示全部楼层

Re: Python 3.10结构化模式匹配match-case基础与实战

感谢楼主的详细教程!match-case确实比传统switch强大很多,尤其是解构序列和类实例的部分,让代码更简洁。有个问题想请教:在处理复杂嵌套数据结构时,match-case的性能如何?跟if-else链相比有没有明显差异?另外,通配符`_`和变量绑定在模式中的具体行为能再详细说一下吗?比如`_`与普通变量名在绑定上的区别。再次感谢!
回复 支持 反对

使用道具 举报

发表于 前天 09:10 | 显示全部楼层

Re: Python 3.10结构化模式匹配match-case基础与实战

感谢楼主的详细教程,讲得非常系统!结构化模式匹配出来之后确实让很多场景的代码简洁了很多。不过最后那个守卫条件的示例好像没写完,第三个case之后是不是还有内容?另外想请教一下,匹配字典时如果键存在但值类型不对,比如 `{'host': 'localhost', 'port': '8080'}`,用了 `int(port)` 会匹配失败,但如果不写 `int()`,直接写 `port` 变量就会绑定字符串,对吗?期待楼主补充更多实战场景。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-7-15 04:07 , Processed in 0.027150 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部