内容提要
PEP 845 提议在 match 语句中允许以点号前缀的简单名称(如 `.author`)作为值模式,按正常名称解析规则查找并比较,而非捕获。这解决了匹配局部变量、全局变量和哨兵值时需用 guard 子句的麻烦,语法向后兼容,社区投票中 71% 支持点号方案。
延伸解读
点号前缀的解析规则
PEP 845 规定,以点号前缀的简单名称(如 .author)在 match 语句中按正常名称解析规则查找,即依次在局部、闭包、全局和内置作用域中搜索,然后与匹配主体进行相等比较。点号仅表示该模式执行查找而非绑定,并不限定作用域。这与相对导入中的点号含义不同,后者表示相对于当前包。
与现有写法的对比
此前要匹配局部变量或全局变量,需使用捕获模式加 guard 子句,例如 case {"author": book_author} if book_author == author。这种写法在复杂模式中可读性差,且无法直接匹配哨兵值。点号方案允许直接写 case {"author": .author},语法更简洁,且完全向后兼容,因为 .name 此前会引发 SyntaxError。
社区投票与替代方案
在伴随讨论的投票中,71% 的受访者支持前导点号语法,高于其他替代方案。其他被考虑过的标记包括 ^CONSTANT、$CONSTANT、?CONSTANT、==CONSTANT、{CONSTANT} 和反引号,但它们的支持率均低于点号。点号已是现有值模式的一部分,且不会与映射模式或比较运算符混淆。
对内置常量和类型匹配的影响
由于点号模式按正常名称解析,内置作用域中的名称也可被匹配。例如 NotImplemented 和 Ellipsis 是普通名称而非关键字,裸用时会成为捕获模式,但写成 .NotImplemented 或 .Ellipsis 即可作为值模式。类似地,case .int: 匹配类型对象本身,而 case int(): 匹配 int 的实例,两者语义不同。
Q&A
PEP 845 提议在 match 语句中增加什么新语法?
PEP 845 提议允许在 match 语句的值模式中使用以点号前缀的简单名称(如 `.author`),该名称会按照正常的名称解析规则查找并与匹配主题进行相等比较,而不是作为捕获模式。
为什么需要前导点值模式?它解决了什么问题?
在 PEP 634 中,值模式只支持带点的属性(如 `Color.RED`),无法直接匹配局部变量、全局变量或哨兵值,必须使用 guard 子句等变通方法,导致代码难以阅读和编写。前导点值模式允许直接匹配这些值,无需 guard 子句。
前导点值模式的语法规则是什么?
值模式扩展为支持以点号前缀的简单名称,语法为 `.NAME`,其中点号后必须恰好跟一个标识符,不允许属性链(如 `.ns.CONST` 无效)。映射模式的键也可以使用前导点名称。查找遵循标准 Python 名称解析规则。
前导点值模式与现有的值模式规则如何统一?
现有规则是“值模式中包含点表示查找;不带点的名称模式表示绑定”。前导点形式将这一规则扩展到非限定名称,即“名称模式中任何位置出现点都表示查找”。
社区对前导点语法的支持度如何?
在伴随讨论的投票中,71% 的受访者支持前导点语法,超过其他替代方案和现状。
前导点值模式是否向后兼容?
是的,该变更完全向后兼容。此前使用 `.name` 会引发 SyntaxError,因此不会影响现有代码。
前导点值模式有哪些替代方案被考虑过?
替代方案包括 `^CONSTANT`、`$CONSTANT`、`?CONSTANT`、`==CONSTANT`、`{CONSTANT}`、反引号,以及 `case value MISSING:`、`case constant MISSING:` 等关键字形式,还有使用 `global.NAME`、`nonlocal.NAME` 等伪命名空间。这些方案在投票中获得的批准均少于前导点形式。
前导点值模式如何处理名称解析作用域?
点号不选择作用域,名称按照普通表达式中的解析规则进行解析(局部、闭包、全局、内置作用域)。点号仅决定模式是执行查找还是绑定。
前导点值模式可以用于匹配内置常量如 NotImplemented 吗?
可以。由于名称解析包括内置作用域,前导点形式允许匹配那些通常只能作为裸名称使用的值,例如 `NotImplemented`、`Ellipsis` 以及内置类型对象(如 `.int` 匹配类型对象本身,而非实例)。