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 socket C/S结构的聊天室应用实现
Nov 30 Python
详解Python中的序列化与反序列化的使用
Jun 30 Python
Phantomjs抓取渲染JS后的网页(Python代码)
May 13 Python
实现python版本的按任意键继续/退出
Sep 26 Python
简单谈谈Python流程控制语句
Dec 04 Python
Python输出带颜色的字符串实例
Oct 10 Python
浅谈Django中的数据库模型类-models.py(一对一的关系)
May 30 Python
Python实现的微信支付方式总结【三种方式】
Apr 13 Python
Python Lambda函数使用总结详解
Dec 11 Python
python+selenium+Chrome options参数的使用
Mar 18 Python
python turtle绘图命令及案例
Nov 23 Python
Python捕获、播放和保存摄像头视频并提高视频清晰度和对比度
Apr 14 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.ini中文版(1)
2006/10/09 PHP
用 PHP5 轻松解析 XML
2006/12/04 PHP
shopex中集成的站长统计功能的代码简单分析
2011/08/11 PHP
163的邮件用phpmailer发送(实例详解)
2013/06/24 PHP
PHP工程师VIM配置分享
2015/12/15 PHP
php获取文件后缀的9种方法
2016/03/22 PHP
jQuery中文入门指南,翻译加实例,jQuery的起点教程
2007/01/13 Javascript
使用JQuery和s3captche实现一个水果名字的验证
2009/08/14 Javascript
jQuery实现的Email中的收件人效果(按del键删除)
2011/03/20 Javascript
2014最热门的JavaScript代码高亮插件推荐
2014/11/25 Javascript
javascript实现数组内值索引随机化及创建随机数组的方法
2015/08/10 Javascript
Angular2库初探
2017/03/01 Javascript
Bootstrap实现的标签页内容切换显示效果示例
2017/05/25 Javascript
nodejs集成sqlite使用示例
2017/06/05 NodeJs
浅谈vuepress 踩坑记
2018/04/18 Javascript
老生常谈JavaScript获取CSS样式的方法(兼容各浏览器)
2018/09/19 Javascript
微信小程序 (地址选择1)--选取搜索地点并显示效果
2019/12/17 Javascript
Vue 一键清空表单的实现方法
2020/02/07 Javascript
JavaScript文档加载模式以及元素获取
2020/07/28 Javascript
vue路由切换时取消之前的所有请求操作
2020/09/01 Javascript
[57:55]EG vs Fnatic 2018国际邀请赛小组赛BO2 第一场 8.19
2018/08/21 DOTA
python使用正则表达式分析网页中的图片并进行替换的方法
2015/03/26 Python
Python常用库推荐
2016/12/04 Python
Python实现的括号匹配判断功能示例
2018/08/25 Python
解决pycharm安装后代码区不能编辑的问题
2018/10/28 Python
python 实现将文件或文件夹用相对路径打包为 tar.gz 文件的方法
2019/06/10 Python
基于Python生成个性二维码过程详解
2020/03/05 Python
小学运动会广播稿200字(十二篇)
2014/01/14 职场文书
工程采购员岗位职责
2014/03/09 职场文书
博士毕业生自我鉴定范文
2014/04/13 职场文书
合作协议书
2014/04/23 职场文书
领导班子对照检查材料
2014/09/22 职场文书
2014年信用社工作总结
2014/11/25 职场文书
仰望星空观后感
2015/06/10 职场文书
创业方案:赚钱的烧烤店该怎样做?
2019/07/05 职场文书
JVM入门之类加载与字节码技术(类加载与类的加载器)
2021/06/15 Java/Android