提问人:jakevdp 提问时间:2/12/2021 最后编辑:Tomerikoojakevdp 更新时间:10/3/2022 访问量:17857
如何使用存储在变量中的值作为案例模式?
How to use values stored in variables as case patterns?
问:
我正在尝试理解 Python 3.10 中新的结构模式匹配语法。我知道可以匹配这样的文字值:
def handle(retcode):
match retcode:
case 200:
print('success')
case 404:
print('not found')
case _:
print('unknown')
handle(404)
# not found
但是,如果我重构这些值并将其移动到模块级变量中,则会导致错误,因为这些语句现在表示结构或模式而不是值:
SUCCESS = 200
NOT_FOUND = 404
def handle(retcode):
match retcode:
case SUCCESS:
print('success')
case NOT_FOUND:
print('not found')
case _:
print('unknown')
handle(404)
# File "<ipython-input-2-fa4ae710e263>", line 6
# case SUCCESS:
# ^
# SyntaxError: name capture 'SUCCESS' makes remaining patterns unreachable
有没有办法使用 match 语句来匹配存储在变量中的值?
答:
如果要测试的常量是虚线名称,则应将其视为常量,而不是要放入捕获的变量的名称(请参阅 PEP 636 # 与常量和枚举匹配):
class Codes:
SUCCESS = 200
NOT_FOUND = 404
def handle(retcode):
match retcode:
case Codes.SUCCESS:
print('success')
case Codes.NOT_FOUND:
print('not found')
case _:
print('unknown')
虽然,考虑到 python 如何尝试实现模式匹配,我认为对于这种情况,在检查常量值时只使用塔可能更安全、更清晰。if/elif/else
评论
match
if
if
除了使用文字值外,PEP 635 的“值模式”部分还提到了虚线名称的使用或防护项的使用。有关比较,请参见下文:
文本值
def handle(code):
match code:
case 200:
print('success')
case 404:
print('not found')
case _:
print('unknown')
引用:
- https://www.python.org/dev/peps/pep-0635/#literal-patterns
- https://www.python.org/dev/peps/pep-0636/#matching-specific-values
虚线名称
任何带点的名称(即属性访问)都被解释为值模式。
class StatusCodes:
OK = 200
NOT_FOUND = 404
def handle(code):
match code:
case StatusCodes.OK:
print('success')
case StatusCodes.NOT_FOUND:
print('not found')
case _:
print('unknown')
引用:
- https://www.python.org/dev/peps/pep-0635/#value-patterns
- https://www.python.org/dev/peps/pep-0636/#matching-against-constants-and-enums
警卫
[A] guard 是附加到模式的任意表达式,必须计算为“真实”值才能使模式成功。
SUCCESS = 200
NOT_FOUND = 404
def handle(code):
match code:
case status if status == SUCCESS:
print('success')
case status if status == NOT_FOUND:
print('not found')
case _:
print('unknown')
引用:
- https://www.python.org/dev/peps/pep-0635/#guards
- https://www.python.org/dev/peps/pep-0636/#adding-conditions-to-patterns
希望我能帮助阐明为什么裸名在这里以这种方式工作。
首先,正如其他人已经指出的那样,如果您需要将值作为模式的一部分进行匹配,您可以通过以下方式实现:
- 匹配支持的文本,如数字、字符串、布尔值和
None
- 匹配限定(带点)名称
- 在防护装置中使用其他测试(与模式分开的
if
)
我担心我们(PEP 作者)可能犯了一个小错误,在早期教程中包含这个玩具片段......从那以后,它有点病毒式传播。我们的目标是以最简单的模式匹配示例为先导,但我们似乎也给许多人留下了令人困惑的第一印象(尤其是在没有上下文的情况下重复时)。
这些 PEP 标题中最容易被忽视的词是“结构性”。如果您不匹配主题的结构,则结构模式匹配可能不是这项工作的正确工具。
这个功能的设计是由解构驱动的(就像在赋值的LHS上可迭代的解包,但对所有对象都是通用的),这就是为什么我们非常容易地执行提取对象的一部分并将它们绑定到名称的核心功能。我们还决定允许程序员匹配值也很有用,因此我们添加了这些值(条件是,当值被命名时,它们必须用点限定,以便将它们与更常见的提取区分开来)。
Python 的模式匹配从来都不是为了支持这样的 C 样式 switch 语句而设计的;这之前已经为Python提出过两次(并被拒绝),所以我们选择了不同的方向。此外,已经有一种明显的方法可以打开单个值,它更简单、更短,并且适用于每个版本的 Python:一个好的 // 梯子!if
elif
else
SUCCESS = 200
NOT_FOUND = 404
def handle(retcode):
if retcode == SUCCESS:
print('success')
elif retcode == NOT_FOUND:
print('not found')
else:
print('unknown')
handle(404)
(如果你真的关心性能或需要表达式,从字典中调度也是一个不错的选择。
评论
case =SUCCESS:
=
SUCCESS
case .SUCCESS
SUCCESS
match
pattern matching
structural pattern matching
Python 不仅仅是一个简单的 switch 语句。如果你只使用你认为的“变量名称”,它们实际上是捕获模式。根据 PEP 第 634 条中的定义match
除了您可能不应该用于您的用例之外,您还必须通过以下方式之一使用限定(虚线)名称:match
#1 平面物体
statuses = object()
statuses.success = 200
status.not_found = 404
def handle(retcode):
match retcode:
case statuses.success: print("Success")
case statuses.not_found: print("Not found")
#2 面向对象编程
class StatusValues:
success = 200
not_found = 404
def handle(retcode):
match retcode:
case StatusValues.success: print("Success")
case StatusValues.not_found: print("Not found")
#3 简单合格的 locals()/globals() 访问
我开发了 match-ref 库,它允许您访问任何函数内部或外部的任何局部或全局变量,只需使用前缀即可。ref.
from matchref import ref
import random
SUCCESS = 200
NOT_FOUND = 404
def handle(retcode):
random_code = random.randint(600,699)
match retcode:
case ref.SUCCESS: print("Success")
case ref.NOT_FOUND: print("Not found")
case ref.random_code: print("OK, you win!")
如您所见,来自局部和全局命名空间的自动解析变量(按此顺序)。无需额外的设置。ref
如果您不想使用第三方库,您可以在下面看到一个略微相似的无库版本。
#4 无需第三方库即可访问合格的 locals()/globals()
locals(
) 和 globals()
是 Python 中的内置函数,它们返回包含映射到其各自值的所有变量名称。您需要能够使用点语法访问字典的值,因为也不支持字典访问语法。因此,您可以编写以下简单的帮助程序类:dict
match
class GetAttributeDict(dict):
def __getattr__(self, name):
return self[name]
并像这样使用它:
import random
SUCCESS = 200
NOT_FOUND = 404
def handle(retcode):
random_code = random.randint(600, 699)
globs = GetAttributeDict(globals())
locs = GetAttributeDict(locals())
match retcode:
case globs.SUCCESS: print("Success")
case globs.NOT_FOUND: print("Not found")
case locs.random_code: print("OK , you win!")
#5 模块访问
鉴于您似乎打算重用您的状态代码(因为否则您可以将它们内联到您的 s 中),您可以考虑为此使用单独的模块。case
constants.py:
SUCCESS = 200
NOT_FOUND = 404
main.py
import constants
match retcode:
case constants.SUCCESS: ...
...
同样,您可能需要重新考虑是否要使用。match
评论
object()
Python > 可让您更有效地处理案例模式。3.10
|
和语句也可以使用。if
用|
match name:
case "example_111" | "example_222":
return f"Hello {name}"
case _:
return "Bye"
using 语句if
def get_product_info(make, in_dollar):
match make:
case "product_111" if in_dollar:
return "10000 $"
case "product_222" if not in_dollar:
return "10000*73 INR"
case _:
return "error"
有没有办法使用 match 语句来匹配存储在变量中的值?
您可以使用不可变数据类型的其他语言执行此操作。
python 中的 match 语句在可能的情况下将值重新绑定到变量,以便它们匹配。
为避免这种情况,请使用文字或虚线结构。有时,使用警卫或声明更容易。if
评论
SUCCESS