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 OS模块常用函数说明
May 23 Python
python中range()与xrange()用法分析
Sep 21 Python
Python使用PDFMiner解析PDF代码实例
Mar 27 Python
Tensorflow简单验证码识别应用
May 25 Python
Python数据可视化正态分布简单分析及实现代码
Dec 04 Python
基于python神经卷积网络的人脸识别
May 24 Python
解决tensorflow1.x版本加载saver.restore目录报错的问题
Jul 26 Python
python去除拼音声调字母,替换为字母的方法
Nov 28 Python
神经网络相关之基础概念的讲解
Dec 29 Python
Python如何批量获取文件夹的大小并保存
Mar 31 Python
Tensorflow与RNN、双向LSTM等的踩坑记录及解决
May 31 Python
Python+Pillow+Pytesseract实现验证码识别
May 11 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
用文本文件制作留言板提示(上)
2006/10/09 PHP
PHP面向对象编程快速入门
2006/12/14 PHP
解决控件遮挡问题:关于有窗口元素和无窗口元素
2007/01/28 PHP
php网站地图生成类示例
2014/01/13 PHP
PHP类继承 extends使用介绍
2014/01/14 PHP
php版微信小店API二次开发及使用示例
2016/11/12 PHP
php redis实现对200w用户的即时推送
2017/03/04 PHP
在Laravel5.6中使用Swoole的协程数据库查询
2018/06/15 PHP
兼容ie和firefox js关闭代码
2008/12/11 Javascript
jQuery 1.5.1 发布,全面支持IE9 修复大量bug
2011/02/26 Javascript
showModalDialog在谷歌浏览器下会返回Null的解决方法
2013/11/27 Javascript
javascript创建和存储cookie示例
2014/01/07 Javascript
jQuery中removeAttr()方法用法实例
2015/01/05 Javascript
JS实现拖动滚动条评分的效果代码分享
2016/09/29 Javascript
详解使用nvm安装node.js
2017/07/18 Javascript
vue实现表格增删改查效果的实例代码
2017/07/18 Javascript
浅谈vue+webpack项目调试方法步骤
2017/09/11 Javascript
微信小程序实现列表下拉刷新上拉加载
2020/07/29 Javascript
JS前端知识点总结之内置对象,日期对象和定时器相关操作
2019/07/05 Javascript
layer.msg()去掉默认时间,实现手动关闭的方法
2019/09/12 Javascript
layui默认选中table的CheckBox复选框方法
2019/09/19 Javascript
微信小程序批量监听输入框对按钮样式进行控制的实现代码
2019/10/12 Javascript
[00:43]TI7不朽珍藏III——幽鬼不朽展示
2017/07/15 DOTA
在python中使用正则表达式查找可嵌套字符串组
2017/10/24 Python
杭州SQL浙江浙大网新恩普软件有限公司
2013/07/27 面试题
运动会通讯稿100字
2014/01/31 职场文书
2014政务公开实施方案
2014/02/19 职场文书
学校运动会霸气口号
2014/06/07 职场文书
幼儿园安全工作总结2015
2015/04/20 职场文书
农村环境卫生倡议书
2015/04/29 职场文书
郭明义观后感
2015/06/08 职场文书
解析CSS 提取图片主题色功能(小技巧)
2021/05/12 HTML / CSS
Python办公自动化PPT批量转换操作
2021/09/15 Python
python利用while求100内的整数和方式
2021/11/07 Python
MongoDB连接数据库并创建数据等使用方法
2021/11/27 MongoDB
uniapp 微信小程序 自定义tabBar 导航
2022/04/22 Javascript