Python 基础体系 · 第 11/112 篇。示例统一以 Python 3.14 为语言基线;第三方库使用与其兼容的现代稳定版本,版本敏感行为会单独说明。
Python 结构化模式匹配:match、case、守卫、绑定与陷阱
Python 的 match 语句在 Python 3.10 引入,Python 3.14 中仍然属于标准语言语法。它解决的不是“把多个 if 换成另一种写法”,而是把值的分类、结构检查、数据提取和条件筛选组合进一种声明式控制流中。(docs.python.org)
例如,下面的代码同时完成了三件事:
- 判断输入是否是一个字典;
- 判断字典中的
"type"是否为"user"; - 在匹配成功后,把
"id"和"name"绑定到局部变量。
def describe(message):
match message:
case {"type": "user", "id": user_id, "name": name}:
return f"user {user_id}: {name}"
case {"type": "system", "level": level}:
return f"system level={level}"
case _:
return "unknown message"
理解 match 的关键,不是记住若干特殊符号,而是区分四个概念:
- 模式:描述输入值应当具有什么结构;
- 匹配:模式是否适用于当前值;
- 绑定:匹配过程中把部分值赋给名称;
- 守卫:模式成功后,继续执行一个普通布尔表达式。
1. match 的执行模型
一个 match 语句的基本形式是:
match subject:
case pattern_1:
block_1
case pattern_2 if guard_2:
block_2
case pattern_3:
block_3
形式化地,可以把它看成:
其中:
- 是
match后面的主题值,即 subject; - 是第 个
case的模式; - 是可选守卫;
- 的结果是模式成功或失败;
- 当模式成功且守卫为真时,执行对应代码块。
运行过程按以下顺序进行:
- 先计算主题表达式;
- 从第一个
case开始,依次尝试模式; - 如果模式失败,跳到下一个
case; - 如果模式成功,执行其中的名称绑定;
- 如果存在守卫,再计算守卫表达式;
- 守卫为真时执行代码块;
- 守卫为假时继续尝试后面的
case; - 一旦某个
case被选中,后续case不再尝试。
官方语言参考明确规定,模式失败时,某些子模式可能已经成功,但不能依赖失败匹配后的变量绑定状态;具体行为可以随实现和优化而变化。(docs.python.org)
1.1 主题表达式只产生一个值
value = (1, 2)
match value:
case (1, 2):
print("matched")
如果 match 后面出现逗号,Python 会按照普通元组表达式规则构造元组:
match 1, 2:
case (1, 2):
print("matched")
上面的主题值也是 (1, 2),而不是先匹配 1、再匹配 2。主题表达式的计算结果始终是一个对象。(docs.python.org)
1.2 match 与 if 的根本区别
if 只需要一个布尔条件:
if message.get("type") == "user":
...
match 的模式则可以同时表达:
case {"type": "user", "id": user_id}:
这相当于要求:
因此,match 不是单纯的相等比较,而是结构匹配加值提取。
2. match 和 case 是软关键字
match 和 case 是 soft keyword,软关键字。它们只有在特定语法位置才具有关键字含义,在普通代码中仍然可以作为变量名。(docs.python.org)
match = "ordinary variable"
case = 42
print(match, case)
但以下代码中,match 和 case 会被解析为模式匹配语法:
match value:
case 1:
...
这与 if、for 等硬关键字不同。软关键字的设计目的,是尽量减少新语法对旧代码的破坏。
3. 模式不是表达式
这是理解结构化模式匹配最重要的前提之一。
在普通表达式中:
x = 1
result = x + 2
x 表示读取变量 x 的值。
但在模式中:
match value:
case x:
...
x 不是读取已有变量,而是一个 捕获模式,表示:
无论
value是什么,都匹配成功,并把它绑定给x。
因此:
def classify(value):
match value:
case x:
return f"matched: {x}"
这个函数对任何输入都会返回匹配结果。
case x 等价于:
x = value
而不是:
value == x
捕获模式总是成功,并遵循赋值表达式相关的作用域规则:在函数中通常会成为最近外层函数作用域的局部变量,除非存在适用的 global 或 nonlocal 声明。(docs.python.org)
4. 字面量模式:真正执行比较的模式
字面量模式包括数字、字符串、None、True 和 False:
def status_text(status):
match status:
case 200:
return "ok"
case 404:
return "not found"
case None:
return "missing"
case _:
return "other"
对于普通数字和字符串,匹配逻辑近似于:
subject == literal
但 None、True 和 False 使用身份判断语义,即分别对应单例对象。(docs.python.org)
match value:
case None:
print("value is None")
case True:
print("value is True")
case False:
print("value is False")
4.1 True、1 和布尔值的陷阱
由于 Python 中:
True == 1
结果为真,很多人会误以为:
match value:
case 1:
...
一定会区分 1 和 True。
实际上,字面量模式对普通值使用相等比较,而布尔字面量使用身份比较。更稳妥的做法是显式安排分支:
def classify(value):
match value:
case True:
return "boolean true"
case 1:
return "integer one"
case _:
return "other"
分支顺序仍然重要。匹配成功后,后面的分支不会再被尝试。
5. 通配模式 _ 与捕获模式的区别
match value:
case _:
return "anything"
_ 是通配模式:
- 总是匹配成功;
- 不绑定任何名称;
- 通常作为最后的兜底分支。
match value:
case _:
print(value) # 这里不能通过 _ 取得主题值
_ 在模式位置是通配符,但在其他位置仍然是普通标识符:
_ = "ordinary name"
match 10:
case _:
print("wildcard")
在 case 代码块中:
_ = "ordinary name"
match 10:
case _:
print(_) # 输出 ordinary name
这里的 _ 不再处于模式位置,而是普通变量读取。官方文档特别强调,_ 只在模式中具有通配含义,在主题表达式、守卫和代码块中仍是普通标识符。(docs.python.org)
如果既想匹配任意值,又想保存整个对象,应使用捕获模式:
match value:
case item:
print(item)
6. 值模式:为什么常量必须写成限定名称
值模式使用带点号的名称,例如:
class Protocol:
HTTP = "http"
HTTPS = "https"
match scheme:
case Protocol.HTTP:
print("HTTP")
case Protocol.HTTPS:
print("HTTPS")
Protocol.HTTP 是值模式,因为它是一个限定名称。解释器会按照普通名称解析规则找到它,然后使用相等比较判断主题值是否相等。(docs.python.org)
6.1 常见错误:直接写常量名
HTTP = "http"
match scheme:
case HTTP:
print("HTTP")
这段代码中的 HTTP 是捕获模式,不是值模式。它会无条件匹配,并把 scheme 绑定到 HTTP。
如果需要匹配常量,应使用:
class Protocol:
HTTP = "http"
match scheme:
case Protocol.HTTP:
...
或者使用枚举成员:
from enum import Enum
class Protocol(Enum):
HTTP = "http"
HTTPS = "https"
def describe(protocol):
match protocol:
case Protocol.HTTP:
return "HTTP"
case Protocol.HTTPS:
return "HTTPS"
case _:
return "unknown"
这里的 Protocol.HTTP 是值模式,而不是捕获模式。
6.2 Enum 与 IntEnum 的差异
普通 Enum 成员不等于其底层值:
from enum import Enum, IntEnum
class Color(Enum):
RED = 1
class Number(IntEnum):
ONE = 1
print(Color.RED == 1) # False
print(Number.ONE == 1) # True
IntEnum 成员同时也是整数,可以参与整数运算,并且与整数相等;普通 Enum 则不会自动表现为底层值。(docs.python.org)
因此,以下代码的匹配对象不同:
match value:
case Color.RED:
print("Enum member")
case 1:
print("integer one")
对于协议解析、状态机和命令分派,应明确决定匹配的是枚举成员还是序列化后的字符串、整数。
7. 序列模式:按位置匹配结构
序列模式使用方括号或带逗号的圆括号:
def parse_point(value):
match value:
case [x, y]:
return x, y
case _:
raise ValueError("expected a 2-item sequence")
以下对象可以匹配:
parse_point([10, 20])
parse_point((10, 20))
序列模式的基本条件是:
- 主题值必须是序列;
str、bytes和bytearray不作为序列模式匹配;- 没有星号模式时,长度必须完全相等;
- 子模式按从左到右的顺序匹配对应元素。(docs.python.org)
例如:
match [10, 20]:
case [x, y]:
print(x, y)
执行过程是:
主题值:[10, 20]
长度检查:2 == 2,成功
第一个子模式 x:绑定 x = 10
第二个子模式 y:绑定 y = 20
整个模式:成功
7.1 为什么字符串不会被拆开匹配
match "ab":
case [first, second]:
print(first, second)
case _:
print("not a sequence pattern match")
输出:
not a sequence pattern match
虽然字符串支持索引和长度操作,但结构化模式匹配特意排除了字符串、字节串和字节数组,避免 "ab" 被意外解释为两个字符的结构。
7.2 星号模式
星号模式可以吸收任意数量的中间元素:
match [1, 2, 3, 4]:
case [first, *middle, last]:
print(first, middle, last)
输出:
1 [2, 3] 4
绑定规则是:
first = 1
last = 4
middle = [2, 3]
星号模式最多出现一次,并且绑定结果始终是列表:
match [1, 2]:
case [*items]:
print(items) # [1, 2]
星号模式还可以不绑定名称:
match [first, _, last]:
case [first, *_rest, last]:
...
但是一个序列模式中不能出现两个星号:
match value:
case [*head, *tail]: # SyntaxError
...
7.3 固定长度与可变长度
match value:
case [x, y]:
...
要求:
而:
match value:
case [x, *rest]:
...
只要求:
因为 x 至少需要一个元素,rest 可以为空列表。
例如:
def split_first(value):
match value:
case [first, *rest]:
return first, rest
case _:
raise ValueError("empty sequence")
split_first([10, 20, 30])
# (10, [20, 30])
split_first([10])
# (10, [])
8. 映射模式:匹配键,而不是匹配完整字典
映射模式使用花括号:
def read_user(data):
match data:
case {"id": user_id, "name": name}:
return user_id, name
case _:
raise ValueError("invalid user object")
它要求:
- 主题值是映射;
"id"和"name"都存在;- 对应值分别匹配子模式;
- 其他额外键不会导致失败。
因此:
read_user({
"id": 7,
"name": "Ada",
"role": "admin",
})
可以成功。映射模式并不要求字典的键集合与模式完全相等。(docs.python.org)
如果需要获取剩余字段,可以使用 **:
def split_user(data):
match data:
case {"id": user_id, **extra}:
return user_id, extra
case _:
raise ValueError("missing id")
split_user({"id": 7, "name": "Ada", "role": "admin"})
# (7, {"name": "Ada", "role": "admin"})
**extra 必须位于映射模式末尾,并且一个映射模式最多有一个双星号模式。(docs.python.org)
8.1 映射模式不会触发 __missing__
映射模式通过映射的双参数 get() 语义检查键值。它要求键已经存在,不会依赖 __missing__() 或通过 __getitem__() 动态创建缺失值。(docs.python.org)
from collections import defaultdict
data = defaultdict(lambda: "created")
match data:
case {"missing": value}:
print(value)
case _:
print("not matched")
即使 defaultdict 具有默认工厂,缺失键也不会因为模式匹配而自动生成。
8.2 映射模式中的键必须是字面量或值模式
键位置不能写任意捕获模式:
match data:
case {key: value}: # SyntaxError
...
这是因为映射模式必须知道要查找哪个键。合法形式包括:
match data:
case {"type": message_type}:
...
或者:
class Keys:
TYPE = "type"
match data:
case {Keys.TYPE: message_type}:
...
9. 类模式:isinstance、属性提取与 __match_args__
类模式用于同时检查对象类型并读取属性:
class User:
def __init__(self, user_id, name):
self.user_id = user_id
self.name = name
def describe(value):
match value:
case User(user_id, name):
return f"{user_id}: {name}"
case _:
return "not a user"
类模式大致执行以下步骤:
- 检查模式中的
User是否是类型对象; - 执行类似
isinstance(value, User)的检查; - 读取匹配所需的属性;
- 对属性值继续执行子模式匹配。(docs.python.org)
9.1 关键字类模式
使用关键字模式可以直接写出属性名称:
match value:
case User(user_id=user_id, name=name):
...
这通常比位置模式更稳定,因为它不依赖属性排列顺序。
9.2 位置类模式与 __match_args__
case User(user_id, name):
并不是自动调用构造函数,也不是把对象当作元组拆包。它会读取:
User.__match_args__
然后把位置参数转换成关键字属性。假设:
User.__match_args__ == ("user_id", "name")
那么:
case User(user_id, name):
逻辑上近似于:
case User(user_id=user_id, name=name):
如果位置模式数量超过 __match_args__ 的长度,或者 __match_args__ 不是字符串元组,就会产生 TypeError。(docs.python.org)
可以手动定义:
class Point:
__match_args__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = y
match Point(1, 2):
case Point(x, y):
print(x, y)
这里的 x 和 y 是模式中的新绑定名称。
9.3 dataclass 与类模式
dataclass 很适合配合类模式,因为它提供清晰的数据模型:
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
def quadrant(point):
match point:
case Point(x, y) if x > 0 and y > 0:
return "第一象限"
case Point(x, y) if x < 0 and y > 0:
return "第二象限"
case Point(x, y) if x < 0 and y < 0:
return "第三象限"
case Point(x, y) if x > 0 and y < 0:
return "第四象限"
case Point(0, 0):
return "原点"
case Point():
return "坐标轴上"
case _:
return "not a point"
对于这种位置匹配,字段声明顺序会影响位置参数含义。若模型后续频繁演进,关键字模式通常更明确:
match point:
case Point(x=x, y=y) if x > 0 and y > 0:
return "第一象限"
dataclass 的 frozen=True 只影响对象属性修改能力,不会让模式匹配变成“按值不可变比较”:
from dataclasses import dataclass
@dataclass(frozen=True)
class User:
id: int
name: str
user = User(1, "Ada")
match user:
case User(id=user_id, name=name):
print(user_id, name)
模式读取的是属性值;是否允许后续修改对象,是数据类自身的另一个语义问题。
10. 守卫:模式成功后的第二道条件
守卫是 case 模式后的 if 表达式:
match value:
case int(number) if number > 0:
print("positive integer")
case int(number):
print("non-positive integer")
守卫不是模式的一部分,而是模式成功后才执行的普通表达式。
逻辑顺序是:
尝试 int(number)
├── 失败:不执行守卫,尝试下一个 case
└── 成功:number 已绑定
├── number > 0 为真:执行代码块
└── number > 0 为假:尝试下一个 case
官方规则保证:模式成功后,守卫执行时,模式中的名称绑定已经完成。守卫按 case 顺序执行;守卫抛出的异常不会被 match 自动吞掉,而是向外传播。(docs.python.org)
10.1 守卫可以使用绑定变量
match response:
case {"status": status, "body": body} if status == 200:
return body
case {"status": status} if status >= 400:
raise RuntimeError(f"HTTP error: {status}")
case _:
raise ValueError("invalid response")
status 和 body 来自模式,守卫负责表达不能仅靠结构描述的业务条件。
10.2 守卫不是异常处理机制
下面的代码不会把 KeyError 自动转成匹配失败:
def parse(value):
match value:
case {"items": items} if items[0] > 0:
return items
case _:
return []
如果 items 是空列表,items[0] 会抛出 IndexError。守卫为表达式,表达式中的异常会正常传播。应显式处理结构:
def parse(value):
match value:
case {"items": [first, *rest]} if first > 0:
return [first, *rest]
case _:
return []
11. as 模式:既检查结构,又保存整体对象
as 模式用于同时匹配子模式并绑定完整主题值:
match value:
case {"type": "user", "id": user_id} as user:
print(user_id)
print(user)
成功后:
user_id = user["id"]
user = 整个字典对象
其逻辑是:
先匹配 {"type": "user", "id": user_id}
匹配成功后,再执行 user = subject
as 右侧必须是捕获名称,不能是 _。(docs.python.org)
它适合在既需要局部字段,又需要保留原始对象时使用:
match event:
case {"type": "created", "id": event_id} as raw_event:
audit(raw_event)
return event_id
12. OR 模式:多个形状共享一个分支
OR 模式使用 |:
match command:
case "quit" | "exit" | "q":
return "stop"
case "help" | "?":
return "help"
case _:
return "unknown"
它会依次尝试各个子模式,任意一个成功,整个 OR 模式就成功。
但 OR 模式有一个重要约束:
所有备选模式必须绑定相同的一组名称。
合法:
match value:
case ("ok", result) | ("success", result):
print(result)
非法:
match value:
case ("ok", result) | ("error", message, code):
...
第一个分支绑定 result,第二个分支绑定 message 和 code,成功后代码块无法确定哪些变量存在,因此会产生语法错误。官方规范要求 OR 模式的每个子模式绑定相同名称集合。(docs.python.org)
13. 捕获变量的作用域与泄漏
模式中的绑定不是代码块局部变量。它们通常会在 match 语句之后继续存在:
def f(value):
match value:
case {"id": user_id}:
print(user_id)
return user_id
当匹配成功时,user_id 可以在 match 之后使用。
但这也会造成两个问题。
13.1 没有匹配时,变量可能未定义
def f(value):
match value:
case {"id": user_id}:
pass
return user_id
当输入不包含 "id" 时,user_id 没有绑定,函数会抛出:
UnboundLocalError
应让每条控制路径都返回结果,或者提供兜底分支:
def f(value):
match value:
case {"id": user_id}:
return user_id
case _:
return None
13.2 守卫失败后,名称可能已经绑定
def f(value):
match value:
case int(number) if number > 10:
return "large"
case _:
return f"number={number}"
当 value 是 5 时:
int(number)模式成功;number绑定为5;- 守卫
number > 10失败; - 继续尝试
_; number可能仍为5。
虽然这是语言语义允许的结果,但不应把守卫失败后的残留绑定当作设计接口。更清晰的写法是:
def f(value):
match value:
case int(number) if number > 10:
return "large"
case int(number):
return f"number={number}"
case _:
return "not an integer"
13.3 失败匹配后的绑定不能依赖
def f(value):
match value:
case [x, 0]:
pass
case _:
pass
return x
当输入是 [1, 2] 时,第一项子模式可能已经绑定过 x,但整个模式失败。规范不保证此时 x 是否存在、是否保持原值或是否发生变化。(docs.python.org)
因此,模式绑定应只在对应成功分支中使用,不要把它当作一种“试探性赋值”。
14. 不可穷尽的模式与不可反驳分支
match 不要求必须覆盖所有可能值:
def f(value):
match value:
case 0:
return "zero"
输入 1 时,函数自然执行到末尾并返回 None。
如果需要穷尽处理,应显式写兜底分支:
def f(value):
match value:
case 0:
return "zero"
case _:
return "other"
case _ 是不可反驳分支,因为它必定成功。捕获模式也不可反驳:
case value:
...
不可反驳分支最多一个,并且必须位于最后;否则后面的分支永远不可达。(docs.python.org)
下面的代码会产生语法错误或被静态检查工具报告为不可达:
match value:
case _:
return "anything"
case 1:
return "one"
带守卫的捕获模式不属于无条件不可反驳分支:
match value:
case item if item is not None:
return "non-null"
case _:
return "other"
因为守卫可能为假。
15. 模式优先级就是业务规则优先级
case 按书写顺序匹配,因此分支顺序会改变程序含义:
def classify(value):
match value:
case int(number):
return "integer"
case 1:
return "one"
case _:
return "other"
classify(1) 返回 "integer",因为 int(number) 已经成功,case 1 根本不会执行。
应该把更具体的模式放在更宽泛的模式之前:
def classify(value):
match value:
case 1:
return "one"
case int(number):
return "integer"
case _:
return "other"
可以把模式排序理解为集合包含关系:
如果第一个模式覆盖的输入集合包含第二个模式,那么第二个模式必须先写,否则它会被遮蔽。
例如:
case {"type": kind}:
case {"type": "user"}:
第一个模式接受所有包含 "type" 的映射,因此第二个分支永远无法处理 "user"。
正确顺序是:
case {"type": "user"}:
case {"type": kind}:
16. 一个完整的事件分派示例
下面使用 Enum、dataclass、映射模式、类模式、OR 模式和守卫组成一个完整示例。
from dataclasses import dataclass
from enum import Enum
class EventType(Enum):
USER_CREATED = "user_created"
USER_DELETED = "user_deleted"
@dataclass(frozen=True)
class UserCreated:
user_id: int
name: str
@dataclass(frozen=True)
class UserDeleted:
user_id: int
def handle(event):
match event:
case UserCreated(user_id=user_id, name=name) if user_id > 0:
return f"create user {user_id}: {name}"
case UserDeleted(user_id=user_id) if user_id > 0:
return f"delete user {user_id}"
case {"type": EventType.USER_CREATED.value, "user_id": user_id, "name": name}:
return f"create serialized user {user_id}: {name}"
case {"type": EventType.USER_DELETED.value, "user_id": user_id}:
return f"delete serialized user {user_id}"
case ("quit" | "exit",):
return "stop"
case _:
raise ValueError(f"unsupported event: {event!r}")
测试:
events = [
UserCreated(1, "Ada"),
UserDeleted(2),
{
"type": "user_created",
"user_id": 3,
"name": "Grace",
},
("quit",),
]
for event in events:
print(handle(event))
输出:
create user 1: Ada
delete user 2
create serialized user 3: Grace
stop
每个分支承担不同职责:
UserCreated(...):处理内存中的领域对象;UserDeleted(...):处理另一种领域对象;- 字典模式:处理序列化或外部输入;
- 元组模式:处理简单命令;
_:拒绝未支持的输入。
这里没有把所有逻辑压缩到一个巨大模式中,而是让数据形状和业务条件分别表达:
case UserCreated(user_id=user_id, name=name) if user_id > 0:
类型和字段由模式处理,user_id > 0 由守卫处理。
17. 模式匹配与 if 的边界
match 适合描述输入的形状差异:
match message:
case {"type": "user", "id": user_id}:
...
case {"type": "system", "level": level}:
...
if 更适合描述同一结构内部的普通条件:
match message:
case {"type": "user", "id": user_id}:
if user_id > 0:
...
也可以使用守卫:
match message:
case {"type": "user", "id": user_id} if user_id > 0:
...
两者的区别在于,守卫适合一个简短、直接依赖绑定变量的条件;如果条件包含复杂副作用、异常处理或多步流程,普通代码通常更容易诊断。
例如,不要把一段长流程塞入守卫:
match request:
case {"user_id": user_id} if validate_user(load_user(user_id)):
...
这段代码同时隐藏了:
load_user()是否有副作用;load_user()是否可能抛异常;validate_user()是否昂贵;- 守卫失败后是否继续尝试其他分支。
可以拆开:
match request:
case {"user_id": user_id}:
user = load_user(user_id)
if validate_user(user):
return process(user)
return reject(request)
这样仍然使用模式匹配完成结构识别,但将可能失败的业务流程放到普通语句中。
18. 常见错误与诊断方法
错误一:把捕获模式当成常量
OK = "ok"
match result:
case OK:
...
问题:OK 是捕获模式。
修复:
class Status:
OK = "ok"
match result:
case Status.OK:
...
或者:
match result:
case "ok":
...
错误二:把映射模式当成完整字典比较
match data:
case {"id": user_id}:
...
这不是要求字典只有一个 "id" 键,而是要求至少包含 "id"。如果必须拒绝额外字段,应显式检查:
match data:
case {"id": user_id, **extra} if not extra:
...
错误三:把类模式当成构造函数调用
case User(user_id, name):
它不会创建 User 对象,也不会调用构造函数,而是检查主题对象是否为 User 实例,并读取属性。类模式的定位是类型和属性匹配。(docs.python.org)
错误四:忽略 __match_args__
@dataclass
class User:
name: str
user_id: int
此时:
case User(a, b):
位置绑定通常对应:
a = name
b = user_id
不要仅凭构造函数调用习惯猜测位置模式含义。需要稳定性时,使用关键字模式:
case User(name=name, user_id=user_id):
错误五:在兜底分支后继续写分支
match value:
case _:
return "default"
case 1:
return "one"
case _ 必定成功,后面的分支不可达。把具体模式放在前面。
错误六:依赖失败匹配后的变量
match value:
case [x, y, 0]:
...
case _:
print(x)
如果第一个模式失败,不应假设 x、y 没有绑定,也不应假设它们保持原值。失败匹配的中间绑定状态不是可靠接口。(docs.python.org)
19. 如何选择模式形式
同一个问题可以有多种写法。选择时,首先看输入的稳定边界。
外部 JSON 或字典输入
优先使用映射模式:
match payload:
case {"kind": "created", "id": int(object_id)}:
...
它可以同时检查键存在、值类型和嵌套结构。
内部领域对象
优先使用类模式:
match command:
case CreateUser(user_id=user_id, name=name):
...
它比对字典键名的依赖更少,且能利用类型模型表达领域结构。
固定位置协议
使用序列模式:
match message:
case ["OK", request_id, *parts]:
...
但位置协议对顺序敏感,字段扩展可能破坏调用方。
常量集合
使用限定值模式或枚举:
match state:
case State.RUNNING | State.PAUSED:
...
不要使用裸名称伪装常量。
复杂业务条件
让模式完成结构筛选,让守卫或普通 if 完成业务判断:
match order:
case Order(total=total, status=status) if status is Status.PAID and total > 0:
...
如果守卫变成多次函数调用、异常处理或副作用流程,应拆出普通语句。
20. 最终应形成的心智模型
可以把每个 case 理解为以下四阶段:
主题值
│
▼
模式检查 ──失败──> 下一个 case
│
▼
名称绑定
│
▼
守卫检查 ──失败──> 下一个 case
│
▼
执行代码块
其中最容易出错的地方有四个:
- 裸名称是捕获,不是常量;
_只在模式位置是通配符;- 守卫在模式成功、绑定完成后才执行;
- 失败匹配后的绑定状态不可依赖。
掌握这些规则后,match 就不再只是语法糖,而是一种可以明确表达输入结构的控制流工具:字面量负责比较,序列负责位置,映射负责键,类模式负责类型和属性,捕获负责取值,as 负责保存整体,OR 模式负责合并形状,守卫负责附加条件,_ 负责明确兜底。
系列导航与关联阅读
- 系列入口:Python 完整学习路线:从语言模型、并发到 Web、数据、AI 与生产交付
- 上一篇:Python 控制流:if、for、while、break、else 与作用域
- 下一篇:Python 函数与参数:位置、关键字、默认值、解包和返回
- 延伸:Python dataclass 与 Enum:数据模型、不可变性、比较和序列化
官方资料
本文依据 Python 官方文档、相关 PEP 与生态项目官方文档重新梳理;正文、示例与工程清单由 WR BLOG 编写。

评论
0 条讨论