Python中判断子串存在的性能比较及分析总结


Posted in Python onJune 23, 2019

起步

对于子串搜索,Python提供了多种实现方式:in, find, index, __contains__,对其进行性能比较:

import timeit

def in_(s, other):
  return other in s

def contains(s, other):
  return s.__contains__(other)

def find(s, other):
  return s.find(other) != -1

def index(s, other):
  try:
    s.index(other)
  except ValueError:
    return False
  return True

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 在子串不存在时会抛出异常。从源码来看:

static PyObject *
unicode_find(PyObject *self, PyObject *args)
{
  /* initialize variables to prevent gcc warning */
  PyObject *substring = NULL;
  Py_ssize_t start = 0;
  Py_ssize_t end = 0;
  Py_ssize_t result;

  if (!parse_args_finds_unicode("find", args, &substring, &start, &end))
    return NULL;

  if (PyUnicode_READY(self) == -1)
    return NULL;

  result = any_find_slice(self, substring, start, end, 1);

  if (result == -2)
    return NULL;

  return PyLong_FromSsize_t(result);
}

static PyObject *
unicode_index(PyObject *self, PyObject *args)
{
  /* initialize variables to prevent gcc warning */
  Py_ssize_t result;
  PyObject *substring = NULL;
  Py_ssize_t start = 0;
  Py_ssize_t end = 0;

  if (!parse_args_finds_unicode("index", args, &substring, &start, &end))
    return NULL;

  if (PyUnicode_READY(self) == -1)
    return NULL;

  result = any_find_slice(self, substring, start, end, 1);

  if (result == -2)
    return NULL;

  if (result < 0) {
    PyErr_SetString(PyExc_ValueError, "substring not found");
    return NULL;
  }

  return PyLong_FromSsize_t(result);
}

实现方式基本相同,所以在子串存在的时候,两者的性能一致;而当子串不存在时,index 会设置异常,因此涉及异常栈的空间等异常机制,速度上也就慢了一些。

总结

in 的搜索方式性能最佳,可读性也最好,属最佳实践。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

扩展阅读

https://stackoverflow.com/questions/38400370/why-in-is-faster-than-contains

Python 相关文章推荐
Python的math模块中的常用数学函数整理
Feb 04 Python
整理Python 常用string函数(收藏)
May 30 Python
python数据抓取分析的示例代码(python + mongodb)
Dec 25 Python
Python OpenCV 直方图的计算与显示的方法示例
Feb 08 Python
Python装饰器知识点补充
May 28 Python
python保存网页图片到本地的方法
Jul 24 Python
Django中使用Celery的教程详解
Aug 24 Python
Python中如何导入类示例详解
Apr 17 Python
Python使用Tkinter实现滚动抽奖器效果
Jan 06 Python
Pycharm快捷键配置详细整理
Oct 13 Python
python 获取域名到期时间的方法步骤
Feb 10 Python
8g内存用python读取10文件_面试题-python 如何读取一个大于 10G 的txt文件?
May 28 Python
树莓派与PC端在局域网内运用python实现即时通讯
Jun 22 #Python
树莓派采用socket方式文件传输(python)
Jun 22 #Python
树莓派用python中的OpenCV输出USB摄像头画面
Jun 22 #Python
树莓派使用USB摄像头和motion实现监控
Jun 22 #Python
树莓派动作捕捉抓拍存储图像脚本
Jun 22 #Python
python+openCV利用摄像头实现人员活动检测
Jun 22 #Python
树莓派实现移动拍照
Jun 22 #Python
You might like
使用PHP实现下载CSS文件中的图片
2015/12/06 PHP
php 实现Hash表功能实例详解
2016/11/29 PHP
用javascript获取地址栏参数
2006/12/22 Javascript
用jquery实现等比例缩放图片效果插件
2010/07/24 Javascript
javascript中call和apply方法浅谈
2013/09/27 Javascript
js 限制input只能输入数字、字母和汉字等等
2013/12/18 Javascript
jQuery scroll事件实现监控滚动条分页示例
2014/04/04 Javascript
javascript实现按回车键切换焦点
2015/02/09 Javascript
jQuery EasyUI 入门必看
2016/06/03 Javascript
详解JavaScript时间处理之几个月前或几个月后的指定日期
2016/12/21 Javascript
详解Node.js开发中的express-session
2017/05/19 Javascript
详解Webpack DLL用法以及功能
2017/07/11 Javascript
微信小程序 共用变量值的实现
2017/07/12 Javascript
Vue组件的使用教程详解
2018/01/05 Javascript
详解微信小程序开发之formId使用(模板消息)
2019/08/27 Javascript
微信小程序后端(java)开发流程的详细步骤
2019/11/13 Javascript
JavaScript 链表定义与使用方法示例
2020/04/28 Javascript
vue watch监控对象的简单方法示例
2021/01/07 Vue.js
关于Js中new操作符的作用详解
2021/02/21 Javascript
python将xml xsl文件生成html文件存储示例讲解
2013/12/03 Python
python MySQLdb Windows下安装教程及问题解决方法
2015/05/09 Python
解决python 未发现数据源名称并且未指定默认驱动程序的问题
2018/12/07 Python
django的聚合函数和aggregate、annotate方法使用详解
2019/07/23 Python
Python使用requests xpath 并开启多线程爬取西刺代理ip实例
2020/03/06 Python
英国时尚和家居用品零售商:Matalan
2021/02/28 全球购物
递归实现回文判断(如:abcdedbca就是回文,判断一个面试者对递归理解的简单程序)
2013/04/28 面试题
思想汇报范文
2013/11/04 职场文书
家长会主持词开场白
2014/03/18 职场文书
班级寄语大全
2014/04/10 职场文书
2014年入党积极分子党校培训心得体会
2014/07/08 职场文书
质量保证书格式
2015/02/27 职场文书
党员违纪检讨书
2015/05/05 职场文书
2015年副班长工作总结
2015/05/15 职场文书
python 办公自动化——基于pyqt5和openpyxl统计符合要求的名单
2021/05/25 Python
你真的了解redis为什么要提供pipeline功能
2021/06/22 Redis
Python 的演示平台支持 WSGI 接口的应用
2022/04/20 Python