【问题标题】:Python Match Case (Switch) PerformancePython 匹配案例(开关)性能
【发布时间】:2021-09-29 05:55:45
【问题描述】:

我期待 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

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

【问题讨论】:

  • 匹配/案例代码甚至不是 python...
  • @Jab 这是 Python 3.10。
  • 你的期望是错误的。这不是原始数据类型的切换案例。它用于模式匹配。它是*不是开关
  • 还有优化空间(对于所有文字模式,我怀疑可以构建一个查找表),但目前它似乎没有优化。跨度>
  • 使用哈希/索引对此进行优化需要编译器事先知道每个案例值的数据类型(即仅适用于常量)。那将是一个非常狭窄的特殊情况。考虑到目标是模式匹配,而不是语句调度,我怀疑是否需要付出额外的努力。

标签: python python-3.x switch-statement match


【解决方案1】:

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

虽然在最原始的情况下它可能等同于 dict 查找,但通常情况并非如此。案例模式被设计成看起来像普通的 Python 代码,但实际上它们隐藏了 isinsancelen 调用,并且当您看到类似 Node() 的代码时,它们不会执行您期望执行的操作。

本质上,这等同于 if ... elif ... else 语句链。请注意,与之前提出的 switch 语句不同,预计算的调度字典语义不适用于此处。

【讨论】:

    【解决方案2】:

    我尝试使用另一个函数调用 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 长一点。

    【讨论】:

    • 有趣的是,这两个函数生成非常相似的字节码。'
    猜你喜欢
    • 1970-01-01
    • 2020-05-23
    • 1970-01-01
    • 2016-03-23
    • 2018-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多