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 代码,但实际上它们隐藏了 isinsance
和 len
调用,并且不会执行您在看到 [= 这样的代码时期望执行的内容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.
我原以为 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 代码,但实际上它们隐藏了 isinsance
和 len
调用,并且不会执行您在看到 [= 这样的代码时期望执行的内容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.