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实现截屏的函数
Jul 25 Python
python生成器表达式和列表解析
Mar 10 Python
搭建Python的Django框架环境并建立和运行第一个App的教程
Jul 02 Python
Python 安装setuptools和pip工具操作方法(必看)
May 22 Python
Python3简单实例计算同花的概率代码
Dec 06 Python
详解Python异常处理中的Finally else的功能
Dec 29 Python
对python 各种删除文件失败的处理方式分享
Apr 24 Python
python实现远程控制电脑
May 23 Python
如何基于Python获取图片的物理尺寸
Nov 25 Python
Python批量启动多线程代码实例
Feb 18 Python
OpenCV 之按位运算举例解析
Jun 19 Python
python中time tzset()函数实例用法
Feb 18 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
追忆往昔!浅谈收音机的百年发展历史
2021/03/01 无线电
无数据库的详细域名查询程序PHP版(2)
2006/10/09 PHP
php抽象方法和普通方法的区别点总结
2019/10/13 PHP
javascript 简单高效判断数据类型 系列函数 By shawl.qiu
2007/03/06 Javascript
jscript之Open an Excel Spreadsheet
2007/06/13 Javascript
WEB页子窗口(showModalDialog和showModelessDialog)使用说明
2009/10/25 Javascript
js 绑定带参数的事件以及手动触发事件
2010/04/27 Javascript
jQuery学习总结之jQuery事件
2014/06/30 Javascript
JQuery实现表格动态增加行并对新行添加事件
2014/07/30 Javascript
js实现拉幕效果的广告代码
2015/09/02 Javascript
jquery.form.js框架实现文件上传功能案例解析(springmvc)
2016/05/26 Javascript
基于原生JS实现图片裁剪
2016/08/01 Javascript
JS中script标签defer和async属性的区别详解
2016/08/12 Javascript
JS经典正则表达式笔试题汇总
2016/12/15 Javascript
详解使用fetch发送post请求时的参数处理
2017/04/05 Javascript
利用PM2部署node.js项目的方法教程
2017/05/10 Javascript
vue webuploader 文件上传组件开发
2017/09/23 Javascript
JS获取当前地理位置的方法
2017/10/25 Javascript
JS实现轮播图效果
2020/01/11 Javascript
vue 公共列表选择组件,引用Vant-UI的样式方式
2020/11/02 Javascript
[07:09]2014DOTA2国际邀请赛-Newbee再次发威成功晋级决赛
2014/07/19 DOTA
[40:56]2018DOTA2亚洲邀请赛 3.31 小组赛 A组 Liquid vs TNC
2018/04/01 DOTA
Python的组合模式与责任链模式编程示例
2016/02/02 Python
Python使用pip安装pySerial串口通讯模块
2018/04/20 Python
基于DataFrame筛选数据与loc的用法详解
2018/05/18 Python
Python双向循环链表实现方法分析
2018/07/30 Python
对python产生随机的二维数组实例详解
2018/12/13 Python
查看python安装路径及pip安装的包列表及路径
2019/04/03 Python
对python中 math模块下 atan 和 atan2的区别详解
2020/01/17 Python
Wilson体育用品官网:美国著名运动器材品牌
2019/05/12 全球购物
英文版餐饮运营管理求职信
2013/11/06 职场文书
应届生自荐信范文
2014/02/21 职场文书
关于读书的演讲稿400字
2014/08/27 职场文书
婚育证明格式
2015/06/17 职场文书
大学生暑期社会实践的个人总结!
2019/07/17 职场文书
python 实现体质指数BMI计算
2021/05/26 Python