Python 匹配案例(切换)性能

Python Match Case (Switch) Performance

我原以为 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"

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

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)]

如果我们运行每个案例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

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

我试图用另一个函数调用来复制你的实验 match_if :

def match_if(decimal):
    if decimal == '0':
        return "000"
    elif decimal == '1':
        return "001"
    elif decimal == '2':
        return "010"
    elif decimal == '3':
        return "011"
    elif decimal == '4':
        return "100"
    elif decimal == '5':
        return "101"
    elif decimal == '6':
        return "110"
    elif decimal == '7':
        return "111"
    else:
        return "NA"

看来,如果我们使用if、elif、else语句,效率不如match/case方法。这是我的结果:

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


Input: 0 Time: 1.6081502437591553
Input: 1 Time: 1.7993037700653076
Input: 2 Time: 2.094271659851074
Input: 3 Time: 2.3727521896362305
Input: 4 Time: 2.6943907737731934
Input: 5 Time: 2.922682285308838
Input: 6 Time: 3.3238701820373535
Input: 7 Time: 3.569467782974243

结果匹配/案例:

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

 Input: 0 Time: 1.4507110118865967
 Input: 1 Time: 1.745032787322998
 Input: 2 Time: 1.988663911819458
 Input: 3 Time: 2.2570419311523438
 Input: 4 Time: 2.54061222076416
 Input: 5 Time: 2.7649216651916504
 Input: 6 Time: 3.1373682022094727
 Input: 7 Time: 3.3378067016601562

对于为什么会出现这些结果,我没有一个准确的答案,但是这个实验表明,如果我们使用 if 语句,它比 match case 稍微长一点。

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 == ")")

使用这样的代码:

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

虽然在最原始的情况下它可能等同于字典查找,但通常情况下并非如此。案例模式被设计成看起来像普通的 python 代码,但实际上它们隐藏了 isinsancelen 调用,并且不会执行您在看到 [= 这样的代码时期望执行的内容14=].

Essentially this is equivalent to a chain of if ... elif ... else statements. Note that unlike for the previously proposed switch statement, the pre-computed dispatch dictionary semantics does not apply here.