Python 匹配大小写(开关)性能

alr*_*lta 12 python match switch-statement python-3.x

我期望 Python match/case对每个案例有相同的时间访问权限,但似乎我错了。有什么好的解释为什么吗?

让我们使用以下示例:

def match_case(decimal):
    match decimal:
        case '0':
            return "000"
        case '1':
            return "001"
        case '2':
            return "010"
        case '3':
            return "011"
        case '4':
            return "100"
        case '5':
            return "101"
        case '6':
            return "110"
        case '7':
            return "111"
        case _:
            return "NA"
Run Code Online (Sandbox Code Playgroud)

并定义一个快速工具来测量时间:

import time
def measure_time(funcion):
    def measured_function(*args, **kwargs):
        init = time.time()
        c = funcion(*args, **kwargs)
        print(f"Input: {args[1]} Time: {time.time() - init}")
        return c
    return measured_function

@measure_time
def repeat(function, input):
    return [function(input) for i in range(10000000)]
Run Code Online (Sandbox Code Playgroud)

如果我们每次运行10000000每种情况,时间如下:

for i in range(8):
    repeat(match_case, str(i))

# Input: 0 Time: 2.458001136779785
# Input: 1 Time: 2.36093807220459
# Input: 2 Time: 2.6832823753356934
# Input: 3 Time: 2.9995620250701904
# Input: 4 Time: 3.5054492950439453
# Input: 5 Time: 3.815168857574463
# Input: 6 Time: 4.164452791213989
# Input: 7 Time: 4.857251167297363
Run Code Online (Sandbox Code Playgroud)

只是想知道为什么访问时间不同。这不是通过查找表进行优化的吗?请注意,我对具有相等访问时间的其他方式(即使用字典)不感兴趣。

Nic*_*ert 13

我参加聚会迟到了,但我觉得我可以在这个帖子中添加一些有用的东西。
不久前,我在 Medium 上发表了一篇关于 Python 的匹配情况性能的文章,分析了生成的字节代码并执行基准测试和比较。如果您愿意,可以在这里阅读这篇文章。我认为这是值得你花时间的。
不过,我还是要在这里总结一下。

许多编程语言将它们的 switch-case 语句实现为 if-else 序列。有时,可以将 switch-case 优化为高效的查找表,但情况并非总是如此。

在 Python 中,这种优化永远不会执行,因此总是会导致一系列条件检查。

从文章中可以看出 if-else 和 match-case 之间的速度比较:

Average time for match_case:  0.00424 seconds
Average time for if_else:     0.00413 seconds
Run Code Online (Sandbox Code Playgroud)

如您所见,它们几乎相等。
另外,如果您反汇编这两个语句,您会发现它们生成几乎相同的字节代码。

只是需要指出的一个区别是,if-else 检查调用对象的__eq__()方法,而 match-case 在内部进行比较。

最后,这是一个还包括哈希表(字典)的基准测试:

Average time for match_case:  0.00438 seconds
Average time for if_else:     0.00433 seconds
Average time for dict_lookup: 0.00083 seconds
Run Code Online (Sandbox Code Playgroud)

因此,如果您热衷于性能,则应该使用哈希表。尽管 match-case 类似于 C 风格的 switch-case,但事实并非如此:match-case 旨在用于结构模式匹配,而不是用于替换高性能哈希表和查找表。


Dim*_*try 8

与传统的 if-elif-else 链相比, PEP 622中引入的 match/case 语句提供了一种更优雅、更易读的模式匹配方法。它的主要优点在于能够简化复杂的条件逻辑。

传统方法

def is_tuple(node):
    if isinstance(node, Node) and node.children == [LParen(), RParen()]:
        return True
    return (isinstance(node, Node)
            and len(node.children) == 3
            and isinstance(node.children[0], Leaf)
            and isinstance(node.children[1], Node)
            and isinstance(node.children[2], Leaf)
            and node.children[0].value == "("
            and node.children[2].value == ")")
Run Code Online (Sandbox Code Playgroud)

使用匹配/大小写

def is_tuple(node: Node) -> bool:
    match node:
        case Node(children=[LParen(), RParen()]):
            return True
        case Node(children=[Leaf(value="("), Node(), Leaf(value=")")]):
            return True
        case _:
            return False
Run Code Online (Sandbox Code Playgroud)

虽然在最原始的情况下它可能相当于字典查找,但通常情况并非如此。案例模式的设计看起来像普通的 python 代码,但实际上它们隐藏isinstancelen调用,并且不会执行当您看到类似Node().

本质上,这相当于一系列 if ... elif ... else 语句。请注意,与之前提出的 switch 语句不同,预先计算的调度字典语义在这里不适用。