我的印象是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
。
我只是不明白。.
只是为了明确一点,in
和startswith
并不等同;我只是在谈论这样一种情况,即人们试图找到的单词必须是字符串中的第一个。
这是因为您必须查找并调用方法。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
会更可靠。