比较 startswith() .vs. in() 的速度



我的印象是startswith必须比in,原因很简单,in必须做更多的检查(允许要查找的单词在字符串中的任何位置)。但我有我的怀疑,所以我决定timeit.下面给出了计时的代码,您可能会注意到我没有做太多计时;代码相当简单。

import timeit
setup1='''
def in_test(sent, word):
if word in sent:
return True
else:
return False
'''
setup2='''
def startswith_test(sent, word):
if sent.startswith(word):
return True
else:
return False
'''
print(timeit.timeit('in_test("this is a standard sentence", "this")', setup=setup1))
print(timeit.timeit('startswith_test("this is a standard sentence", "this")', setup=setup2))

结果:

>> in:         0.11912814951705597
>> startswith: 0.22812353561129417

所以startswith慢了两倍..鉴于我上面进一步所说的,我发现这种行为非常令人费解。我是否在计时时做错了什么,或者in确实更快?如果是这样,为什么?

请注意,即使它们都返回False,结果也非常相似(在这种情况下,in必须实际遍历整个 sentece,以防它之前只是短路):

print(timeit.timeit('in_test("another standard sentence, would be that", "this")', setup=setup1))
print(timeit.timeit('startswith_test("another standard sentence, would be that", "this")', setup=setup2))
>> in:         0.12854891578786237
>> startswith: 0.2233201940338861

如果我必须从头开始实现这两个函数,它将如下所示(伪代码):

startswith:开始逐个比较单词的字母和句子的字母,直到a)单词耗尽(返回True)或b)检查返回False(返回False)

in:在句子中找到单词首字母的每个位置都调用startswith

我只是不明白。.


只是为了明确一点,instartswith并不等同;我只是在谈论这样一种情况,即人们试图找到的单词必须是字符串中的第一个

这是因为您必须查找并调用方法。in是专门的,直接导致COMPARE_OP(调用cmp_outcome,而str.startswith又调用PySequence_Contains),而则通过较慢的字节码:

2 LOAD_ATTR                0 (startswith)
4 LOAD_FAST                1 (word)
6 CALL_FUNCTION            1              # the slow part

__contains__替换in,强制在这种情况下调用函数,几乎否定了速度差异:

setup1='''
def in_test(sent, word):
if sent.__contains__(word):
return True
else:
return False
'''

而且,时间:

print(timeit.timeit('in_test("this is a standard sentence", "this")', setup=setup1))
print(timeit.timeit('startswith_test("this is a standard sentence", "this")', setup=setup2))
0.43849368393421173
0.4993997460696846

in在这里获胜,因为它不需要经历整个函数调用设置,并且由于它呈现的有利情况。

您正在比较字符串上的运算符与属性查找和函数调用。第二个将具有更高的开销,即使第一个在大量数据上花费很长时间。

此外,您正在寻找第一个单词,因此如果它确实匹配,in将查看与startswith()一样多的数据。要查看差异,您应该查看悲观情况(未找到结果,或在字符串末尾匹配):

setup1='''
data = "xxxx"*1000
def ....
print(timeit.timeit('in_test(data, "this")', setup=setup1))
0.932795189000899
print(timeit.timeit('startswith_test(data, "this")', setup=setup2))
0.22242475600069156

如果你看一下函数产生的字节码:

>>> dis.dis(in_test)
2           0 LOAD_FAST                1 (word)
3 LOAD_FAST                0 (sent)
6 COMPARE_OP               6 (in)
9 POP_JUMP_IF_FALSE       16
3          12 LOAD_CONST               1 (True)
15 RETURN_VALUE
5     >>   16 LOAD_CONST               2 (False)
19 RETURN_VALUE
20 LOAD_CONST               0 (None)
23 RETURN_VALUE

您会注意到有很多开销与字符串匹配没有直接关系。在更简单的函数上进行测试:

def in_test(sent, word):
return word in sent

会更可靠。

最新更新