Python中判断子串存在的性能比较及分析总结
起步
对于子串搜索,Python提供了多种实现方式:in,find,index,__contains__,对其进行性能比较:
importtimeit
defin_(s,other):
returnotherins
defcontains(s,other):
returns.__contains__(other)
deffind(s,other):
returns.find(other)!=-1
defindex(s,other):
try:
s.index(other)
exceptValueError:
returnFalse
returnTrue
perf_dict={
'in:True':min(timeit.repeat(lambda:in_('superstring','str'))),
'in:False':min(timeit.repeat(lambda:in_('superstring','not'))),
'__contains__:True':min(timeit.repeat(lambda:contains('superstring','str'))),
'__contains__:False':min(timeit.repeat(lambda:contains('superstring','not'))),
'find:True':min(timeit.repeat(lambda:find('superstring','str'))),
'find:False':min(timeit.repeat(lambda:find('superstring','not'))),
'index:True':min(timeit.repeat(lambda:index('superstring','str'))),
'index:False':min(timeit.repeat(lambda:index('superstring','not'))),
}
print(perf_dict)
得到结果:
{
'in:True':0.2763608000000001,
'in:False':0.2794432,
'__contains__:True':0.40546490000000013,
'__contains__:False':0.4122471000000001,
'find:True':0.497128,
'find:False':0.4951530000000002,
'index:True':0.5243821999999998,
'index:False':0.8693923999999988
}
从结果上in的搜索方式性能上最好。
知其然也要之其所以然,下面就对于这个结果进行比较与分析。
in与__contains__比较
了解Python中协议的应该知道,in操作其实也是调用__contains__,但为什么in比__contains__明显快了很多,明明它们最终调用的C语言函数是一样的。
在CPython中,in属于操作符,它直接指向了sq_contains中的C级函数指针,而在str中的sq_contains直接指向了最终调用的C层函数。而__contains__的调用方式,则需要先在str属性中进行LOAD_ATTR查找,然后再为CALL_FUNCTION创建函数调用所需的空间。
也就是说,in直接指向了最终的C层函数,一步到位,也不走Python虚拟机的函数调用,而__contains__调用方式先属性查找和Python函数调用的开销;所以str.__contains__(other)的形式要慢得多。
一般来说,in方式更快只使用Python内置的C实现的类。对于用户自定义类,因为最终调用都是Python级的,所以两种方式都要对函数调用所需的空间的。
find与index的比较
find与index的查找方式的区别仅仅只是index在子串不存在时会抛出异常。从源码来看:
staticPyObject*
unicode_find(PyObject*self,PyObject*args)
{
/*initializevariablestopreventgccwarning*/
PyObject*substring=NULL;
Py_ssize_tstart=0;
Py_ssize_tend=0;
Py_ssize_tresult;
if(!parse_args_finds_unicode("find",args,&substring,&start,&end))
returnNULL;
if(PyUnicode_READY(self)==-1)
returnNULL;
result=any_find_slice(self,substring,start,end,1);
if(result==-2)
returnNULL;
returnPyLong_FromSsize_t(result);
}
staticPyObject*
unicode_index(PyObject*self,PyObject*args)
{
/*initializevariablestopreventgccwarning*/
Py_ssize_tresult;
PyObject*substring=NULL;
Py_ssize_tstart=0;
Py_ssize_tend=0;
if(!parse_args_finds_unicode("index",args,&substring,&start,&end))
returnNULL;
if(PyUnicode_READY(self)==-1)
returnNULL;
result=any_find_slice(self,substring,start,end,1);
if(result==-2)
returnNULL;
if(result<0){
PyErr_SetString(PyExc_ValueError,"substringnotfound");
returnNULL;
}
returnPyLong_FromSsize_t(result);
}
实现方式基本相同,所以在子串存在的时候,两者的性能一致;而当子串不存在时,index会设置异常,因此涉及异常栈的空间等异常机制,速度上也就慢了一些。
总结
in的搜索方式性能最佳,可读性也最好,属最佳实践。
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对毛票票的支持。
扩展阅读
https://stackoverflow.com/questions/38400370/why-in-is-faster-than-contains