AI PLAY玩法更新持续收录
← 回到更新流

2026-09-11 · 原文发布日

Python 3.15将软弃用re.match():改用语义更清晰的写法

Python 3.15计划把re.match()标记为软弃用:不删除但不建议新代码再用。它只锚定字符串开头而不检查结尾,名字长期误导人,新版本提供含义更明确的re.prefixmatch()。多数场景其实你想要的可能是re.search()(在整串任意位置找)或re.fullmatch()(整串完全匹配)。写正则时先想清楚锚定范围,能少踩一类隐蔽bug。

原文要点

Python有“软弃用”机制:API被标记为新代码不应再用,但不承诺将来删除。3.15发布经理Hugo van Kemenade介绍,3.15将对re.match()软弃用,并新增语义更清楚的名字re.prefixmatch(),因为它只锚定字符串开头、不约束结尾。多数场景其实应使用re.search()(整串任意位置匹配)或re.fullmatch()(整串完全匹配)。

这次做成什么

盘点自己代码库里的re.match()调用,按真实意图分别迁移到re.prefixmatch/re.search/re.fullmatch,并用对照脚本和回归测试确认行为不变或修正了隐藏bug。

准备什么

  • 一台装有Python 3.10+的机器用于练习三种函数语义
  • 一份含re.match()调用的自有代码库(或本文给的合成示例文件)
  • 若要实际调用re.prefixmatch,需要Python 3.15及以上版本

具体怎么做

  1. 盘点存量调用

    在项目根目录运行本文给出的AST扫描脚本,或直接用 rg "re\.match\(" 搜索,得到每一处调用的文件、行号和传入的正则表达式,导出成清单逐条处理。

  2. 逐条判定真实意图

    对清单里每个调用问一句:我想匹配的是字符串开头(如解析前缀、协议头)、任意位置(如找日志里的错误码)、还是整串校验(如校验ID格式)?把答案记在清单里,这是选替代函数的唯一依据。

  3. 按意图替换写法

    开头匹配:3.15+直接用re.prefixmatch(pattern, s),兼容旧版本可写re.search(r"\A" + pattern, s);任意位置:直接改re.search(pattern, s),并检查原正则是否本来就以^开头(有^则行为不变);整串校验:改re.fullmatch(pattern, s),去掉原来人工加的$或\Z。

  4. 补边界回归测试

    为每个被改动的调用至少写三条用例:目标在开头、目标在中间、整串恰好等于模式。运行测试确认新写法输出符合记录的真实意图,而不是机械等价于旧re.match。

可直接复制的材料

match_usage_scan.py(AST扫描脚本)

本站独立编写的盘点脚本,只读不改;在虚拟环境里运行,路径参数换成你的项目目录。

import ast, sys, pathlib

found = []
for path in pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else ".").rglob("*.py"):
    try:
        tree = ast.parse(path.read_text(encoding="utf-8"))
    except SyntaxError:
        continue
    for node in ast.walk(tree):
        if isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute):
            if node.func.attr == "match" and isinstance(node.func.value, ast.Name) and node.func.value.id == "re":
                pattern_src = ast.unparse(node.args[0]) if node.args else "<动态>"
                found.append(f"{path}:{node.lineno}  re.match({pattern_src}, ...)")
print("\n".join(found) if found else "未发现 re.match 调用")

三函数语义对照示例

本站编写的语义对照练习,任何3.10+版本可运行;prefixmatch行仅为注释说明,3.15以下勿取消注释。

import re

pattern = r"\d{4}-\d{2}-\d{2}"
samples = ["2026-09-11 订单已发货", "备注: 2026-09-11", "2026-09-11"]

for s in samples:
    print(repr(s))
    print("  re.match(同旧语义, 只锚开头):", bool(re.match(pattern, s)))
    print("  re.search(任意位置):        ", bool(re.search(pattern, s)))
    print("  re.fullmatch(整串校验):     ", bool(re.fullmatch(pattern, s)))
    # Python 3.15+ 可改用: re.prefixmatch(pattern, s),与 re.match 同语义

输入与输出示例

本站虚构演示输出,数字与输入一一对应,非实际运行结果。

输入

对三个样本字符串运行对照脚本:"2026-09-11 订单已发货"、"备注: 2026-09-11"、"2026-09-11",模式 r"\d{4}-\d{2}-\d{2}"

对应输出

'2026-09-11 订单已发货' → match=True  search=True  fullmatch=False\n'备注: 2026-09-11' → match=False  search=True  fullmatch=False\n'2026-09-11' → match=True  search=True  fullmatch=True\n结论: 第一行暴露re.match不查结尾的陷阱, 校验日期格式应改用re.fullmatch

做完检查这些

  • 扫描脚本能列出项目里全部re.match调用及行号,清单无遗漏
  • 每个被替换的调用都在清单上标注了三种意图之一并有对应新写法
  • 三个边界用例(开头/中间/整串)在新写法下全部通过回归测试
  • 改用re.fullmatch的位置已删除正则里多余的结尾锚点$或\Z

容易卡住的地方

  • re.match不检查结尾是设计而非bug,旧代码可能依赖此行为,替换前必须确认意图
  • 正则里已有^时再改re.search行为等价,但去掉^的re.search语义不同,别混淆
  • re.prefixmatch只在3.15+存在,低版本解释器调用会直接AttributeError
  • 动态拼接的正则无法被AST脚本打印出模式文本,需人工在清单里补充

原文与作者资料 ↗

本站借助AI整理来源事实,独立编写中文操作指南与示例。示例不代表实际运行结果;来源效果尚未由本站独立复现。付费模板与模型、服务费用分别计算。

原文标题
Soft-deprecating re.match()
原作者
Simon Willison
原始发布日期
2026-09-11
来源
https://simonwillison.net/2026/Sep/11/soft-deprecating-re-match/