Python match 的裸名称:常量已经定义,为什么 case 仍把任意输入都接住
把状态路由从 if 改成 match 时,程序员常把 READY = "ready" 放在函数里,然后写 case READY。看起来是在引用已有常量,实际却会把收到的值绑定给 READY。就算输入是 "broken",这个分支也照样命中。
下面使用 Python 3.10 起提供的模式匹配语法。保存为 demo.py,运行 python demo.py。先让错误写法只保留一个分支,完整观察捕获结果;再通过类属性构造带点名称,检查已知值与未知值走不同路径。
AI模型生成概念示意:捕获框接住输入并获得其形状,固定值筛选框则只接收符合条件的对象;不是代码运行界面。
def capture(value):
READY = "ready"
match value:
case READY:
return READY
class Status:
READY = "ready"
def classify(value):
match value:
case Status.READY:
return "accepted"
case _:
return "unknown"
captured = capture("broken")
assert captured == "broken"
assert classify("ready") == "accepted"
assert classify("broken") == "unknown"
assert classify(None) == "unknown"
print("captured:", captured)
for value in ("ready", "broken", None):
print(repr(value), "->", classify(value))
invalid = "match 'broken':\n case READY: pass\n case _: pass\n"
try:
compile(invalid, "<pattern-demo>", "exec")
except SyntaxError:
print("unreachable fallback: SyntaxError")
else:
raise AssertionError("unreachable fallback compiled")大小写不决定模式含义
首行输出 captured: broken,直接证明原先的 READY 值没有参与相等比较。裸名称是捕获模式:匹配成功时给名称赋值。全大写只是代码风格约定,不会让解释器把这个名称变成常量模式;换成 ready 或 expected 也不会改变这一点。
修正后的三行分别为 'ready' -> accepted、'broken' -> unknown 和 None -> unknown。Status.READY 属于带点名称的值模式,解释器查到属性值后再与输入比较。也可以直接写字符串字面量 case "ready";是否集中定义常量,应按维护需求决定。
无条件捕获会让后续分支失去机会
最后一行输出 unreachable fallback: SyntaxError。程序只编译一段固定的短文本,不执行它:无守卫条件的裸名称已经能接住所有输入,后面的 case _ 永远没有机会,因此编译阶段拒绝这个结构。不能为了消除报错就删除未知状态分支,否则路由仍然把任意输入当作成功。
下划线在模式里表示通配符,不创建同名捕获变量;本例用它作为最后的兜底。若需要拿到未知值供记录,可以改用明确的捕获名称,但要让它留在最后,并给记录内容设置必要的范围,避免将任意输入全部输出。
匹配协议与业务检查分别写清
如果期望值只是一个局部变量,使用普通 if 比较通常更直接。也能先捕获新名称,再在守卫中比较它与期望值,但不要在捕获位置复用期望值的名称,否则比较前就把旧值覆盖了。守卫有额外执行步骤,应避免顺手修改外部状态。
值模式采用相等判断,并不自动提供严格类型检查。这个例子只使用固定字符串与 None,已经足以证明路由差异。接收结构化外部输入时,还应明确类型、字段与允许状态,测试命中和未命中两类样本;只测试 "ready" 一条正常输入,会让原来的捕获错误顺利通过。
资料核对日期:2026年10月2日。示例在本地 Python 3.12.14 实际执行,全部断言通过;输出对应文中固定输入。


