如何使用存储在变量中的值作为案例模式?

我正在尝试了解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 # 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在检查常量值时只使用一个塔可能更安全、更清晰。

  • I think this is only less clear because no body is used to `match` statements yet. I suspect the implementation can perform pattern matching more efficiently than the linear search imposed by an `if` tower.
  • Can you elaborate on "given how python is trying to implement pattern-matching"? What about the how would make it less safe or less clear than if/else?

回答

希望我能帮助阐明为什么裸名称在这里以这种方式工作。

首先,正如其他人已经指出的,如果您需要将值作为模式的一部分进行匹配,您可以通过以下方式进行:

  • 匹配支持的文字,如数字、字符串、布尔值和 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)

(如果您真的很关心性能或需要表达式,从字典中调度也是一个不错的选择。)


回答

除了使用字面值之外,PEP 635的值模式部分还提到了使用虚线名称或使用guards。对比见下图:

字面值

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] 守卫是附加到模式的任意表达式,必须评估为“真实”值才能使模式成功。

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

以上是如何使用存储在变量中的值作为案例模式?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>