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