我本以为Pythonmatch
/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
只是想知道为什么访问时间不同。这不是通过查找表进行了优化吗?。请注意,我对其他具有相等访问时间的方式(即使用字典(不感兴趣。
我参加聚会迟到了,但我觉得我可以为这个线程添加一些有用的东西
不久前,我在Medium上发表了一篇关于Python的匹配情况性能的文章,分析生成的字节码并执行基准测试和比较。如果你愿意,你可以在这里阅读这篇文章。我认为这值得你花时间
不过,我要在这里总结一下。
许多编程语言实现它们的switch-case语句,就好像它们是if-else序列一样。有时,切换情况可以优化为高效的查找表,但情况并非总是如此。
在Python中,这种优化从不执行,因此总是导致一系列条件检查。
从这篇文章中,if else和match case之间的速度比较:
Average time for match_case: 0.00424 seconds
Average time for if_else: 0.00413 seconds
正如你所看到的,他们几乎是平等的
此外,如果分解这两条语句,您会发现它们生成的字节码几乎相同。
需要指出的区别是,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
因此,如果您热衷于性能,则应该使用哈希表。虽然匹配情况类似于C风格的切换情况,但事实并非如此:匹配情况旨在用于结构模式匹配,而不是用于替换性能哈希表和查找表。
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代码,但实际上它们隐藏了isinsance
和len
调用,并且当你看到Node()
这样的代码时,不会执行你期望执行的内容。
本质上,这相当于一个if。。。elif。。。else语句。注意,与之前提出的switch语句不同,这里不应用预先计算的调度字典语义。
我试图用另一个调用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语句,它会比匹配情况长一点。
由于python 3.10,可以使用匹配。。。case,默认选项(称为通配符(用case _
表示
def http_error(status):
match status:
case 400:
return "Bad request"
case 404:
return "Not found"
case 418:
return "I'm a teapot"
case _:
return "Something's wrong with the internet"
文档:https://docs.python.org/3/whatsnew/3.10.html#simple-模式匹配到文字
性能
如果您关心比赛的表现。。。casevsif。。elif。。。否则它们是相同的
正如您所看到的,Python的(andilabs:查看汇编指令(match-case语句只是一系列隐藏的比较,与if-else方法完全相同。这就是为什么当我们对这两种方法进行基准测试时,它们的表现是一样的。
来源:https://betterprogramming.pub/pythons-match-case-is-too-slow-if-you-don-t-understand-it-8e8d0cf927d
使用字典的方法怎么样
关于使用旧的pythonic方法与dictionary切换的附带说明-它更慢,所以使用python3.10+和match... case
,它可能会成为真正的老式方法。