如何使用存储在变量中的值作为案例模式?
How to use values stored in variables as case patterns?
我正在尝试理解 Python 3.10 中新的 structural pattern matching 语法。我知道可以像这样匹配文字值:
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 # Matching against constants and enums):
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
检查常数值时的塔。
除了使用 literal 值外,PEP 635 的 Value Patterns 部分还提到使用 dotted names 或使用 守卫 。比较见下:
文字值
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
点名
Any dotted name (i.e. attribute access) is interpreted as a value pattern.
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 is an arbitrary expression attached to a pattern and that must evaluate to a "truthy" value for the pattern to succeed.
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')
参考文献:
希望我能帮助阐明为什么 裸名在这里如此工作。
首先,正如其他人已经指出的那样,如果您需要匹配值作为模式的一部分,您可以通过以下方式实现:
- 匹配支持的文字,例如数字、字符串、布尔值和
None
- 匹配合格(点)名称
- 在守卫中使用额外的测试(与模式分开
if
)
我担心我们(PEP 作者)可能犯了一个小错误,在早期教程中包含了这个玩具片段……它已经有点病毒式传播了。我们的目标是以最简单的模式匹配示例作为引导,但我们似乎也给许多人造成了令人困惑的第一印象(尤其是在没有上下文的情况下重复时)。
这些 PEP 标题中最容易被忽视的词是“结构”。如果您不匹配主题的 结构,结构 模式匹配可能不是完成这项工作的正确工具。
此功能的设计是由解构驱动的(例如在赋值的 LHS 上进行迭代拆包,但对所有 objects 进行了概括),这就是为什么我们使执行核心功能变得非常容易提取 object 的一部分并将它们绑定到名称。我们 也 认为允许程序员匹配值也是有用的,所以我们添加了那些(条件是在命名值时,它们必须用点限定,为了将它们与更常见的提取物区分开来。
Python 的模式匹配从来没有真正设计用于支持 C-style 这样的 switch 语句;之前曾两次为 Python 提出(并被拒绝),因此我们选择了不同的方向。此外,已经有一个明显的方法来打开单个值,它更简单、更短,并且适用于 Python 的每个版本:a good-ol' 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)
(如果您真的很关心性能或需要表达式,从字典中调度也是一个不错的选择。)
Python 的 match
不仅仅是一个简单的 switch 语句。如果你只使用你认为的“变量名”,它们实际上将是 Capture Patterns. as per definition in PEP no. 634
除了您可能不应该为您的用例使用 match
之外,您还必须通过以下方式之一使用 qualified(点分)名称:
#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() 访问
我开发了 the match-ref library,它允许您访问任何函数内部或外部的任何局部或全局变量,只需使用 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
自动解析了本地和全局命名空间中的变量(按此顺序)。无需额外设置。
如果你不想使用第 3 方库,你可以在下面看到一个稍微类似的无库版本。
#4 没有第 3 方库的合格 locals()/globals() 访问
locals()
and globals()
是 Python 中的内置函数,其中 return 一个 dict
包含映射到它们各自值的所有变量名。您需要能够使用点分语法访问字典的值,因为 match
也不支持字典访问语法。因此,您可以编写这个简单的助手 class:
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 模块访问
鉴于您似乎打算重新使用您的状态代码(因为否则您可以将它们内联到您的 case
中),您可以考虑为此使用单独的模块。
constants.py:
SUCCESS = 200
NOT_FOUND = 404
main.py
import constants
match retcode:
case constants.SUCCESS: ...
...
同样,您可能需要重新考虑是否要使用 match
。
python > 3.10
让您更有效地处理大小写模式。
|
和 if
语句也可以使用。
使用|
match name:
case "example_111" | "example_222":
return f"Hello {name}"
case _:
return "Bye"
使用 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"
我正在尝试理解 Python 3.10 中新的 structural pattern matching 语法。我知道可以像这样匹配文字值:
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 # Matching against constants and enums):
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
检查常数值时的塔。
除了使用 literal 值外,PEP 635 的 Value Patterns 部分还提到使用 dotted names 或使用 守卫 。比较见下:
文字值
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
点名
Any dotted name (i.e. attribute access) is interpreted as a value pattern.
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 is an arbitrary expression attached to a pattern and that must evaluate to a "truthy" value for the pattern to succeed.
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')
参考文献:
希望我能帮助阐明为什么 裸名在这里如此工作。
首先,正如其他人已经指出的那样,如果您需要匹配值作为模式的一部分,您可以通过以下方式实现:
- 匹配支持的文字,例如数字、字符串、布尔值和
None
- 匹配合格(点)名称
- 在守卫中使用额外的测试(与模式分开
if
)
我担心我们(PEP 作者)可能犯了一个小错误,在早期教程中包含了这个玩具片段……它已经有点病毒式传播了。我们的目标是以最简单的模式匹配示例作为引导,但我们似乎也给许多人造成了令人困惑的第一印象(尤其是在没有上下文的情况下重复时)。
这些 PEP 标题中最容易被忽视的词是“结构”。如果您不匹配主题的 结构,结构 模式匹配可能不是完成这项工作的正确工具。
此功能的设计是由解构驱动的(例如在赋值的 LHS 上进行迭代拆包,但对所有 objects 进行了概括),这就是为什么我们使执行核心功能变得非常容易提取 object 的一部分并将它们绑定到名称。我们 也 认为允许程序员匹配值也是有用的,所以我们添加了那些(条件是在命名值时,它们必须用点限定,为了将它们与更常见的提取物区分开来。
Python 的模式匹配从来没有真正设计用于支持 C-style 这样的 switch 语句;之前曾两次为 Python 提出(并被拒绝),因此我们选择了不同的方向。此外,已经有一个明显的方法来打开单个值,它更简单、更短,并且适用于 Python 的每个版本:a good-ol' 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)
(如果您真的很关心性能或需要表达式,从字典中调度也是一个不错的选择。)
Python 的 match
不仅仅是一个简单的 switch 语句。如果你只使用你认为的“变量名”,它们实际上将是 Capture Patterns. as per definition in PEP no. 634
除了您可能不应该为您的用例使用 match
之外,您还必须通过以下方式之一使用 qualified(点分)名称:
#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() 访问
我开发了 the match-ref library,它允许您访问任何函数内部或外部的任何局部或全局变量,只需使用 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
自动解析了本地和全局命名空间中的变量(按此顺序)。无需额外设置。
如果你不想使用第 3 方库,你可以在下面看到一个稍微类似的无库版本。
#4 没有第 3 方库的合格 locals()/globals() 访问
locals()
and globals()
是 Python 中的内置函数,其中 return 一个 dict
包含映射到它们各自值的所有变量名。您需要能够使用点分语法访问字典的值,因为 match
也不支持字典访问语法。因此,您可以编写这个简单的助手 class:
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 模块访问
鉴于您似乎打算重新使用您的状态代码(因为否则您可以将它们内联到您的 case
中),您可以考虑为此使用单独的模块。
constants.py:
SUCCESS = 200
NOT_FOUND = 404
main.py
import constants
match retcode:
case constants.SUCCESS: ...
...
同样,您可能需要重新考虑是否要使用 match
。
python > 3.10
让您更有效地处理大小写模式。
|
和 if
语句也可以使用。
使用|
match name:
case "example_111" | "example_222":
return f"Hello {name}"
case _:
return "Bye"
使用 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"