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实现批量重命名文件的代码
May 25 Python
python中的一些类型转换函数小结
Feb 10 Python
Python使用django获取用户IP地址的方法
May 11 Python
使用Python的urllib和urllib2模块制作爬虫的实例教程
Jan 20 Python
Python中文编码知识点
Feb 18 Python
Python qqbot 实现qq机器人的示例代码
Jul 11 Python
python 3.7.4 安装 opencv的教程
Oct 10 Python
ipad上运行python的方法步骤
Oct 12 Python
python实现文件批量编码转换及注意事项
Oct 14 Python
Django restframework 框架认证、权限、限流用法示例
Dec 21 Python
基于python的docx模块处理word和WPS的docx格式文件方式
Feb 13 Python
pytorch--之halfTensor的使用详解
May 24 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
Thinkphp多文件上传实现方法
2014/10/31 PHP
php获取本周星期一具体日期的方法
2015/04/20 PHP
PHP入门教程之面向对象的特性分析(继承,多态,接口,抽象类,抽象方法等)
2016/09/11 PHP
php array_multisort 对数组进行排序详解及实例代码
2016/10/27 PHP
JavaScript While 循环基础教程
2007/04/05 Javascript
不使用中间变量,交换int型的 a, b两个变量的值。
2010/10/29 Javascript
javascript天然的迭代器
2010/10/29 Javascript
DD_belatedPNG,IE6下PNG透明解决方案(国外)
2010/12/06 Javascript
15个款优秀的 jQuery 图片特效插件推荐
2011/11/21 Javascript
批量下载对路网图片并生成html的实现方法
2016/06/07 Javascript
bootstrap按钮插件(Button)使用方法解析
2017/01/13 Javascript
Vue模拟数据,实现路由进入商品详情页面的示例
2018/08/31 Javascript
vue简单封装axios插件和接口的统一管理操作示例
2020/02/02 Javascript
es6函数之尾调用优化实例分析
2020/04/25 Javascript
javaScript代码飘红报错看不懂?读完这篇文章再试试
2020/08/19 Javascript
[31:47]夜魇凡尔赛茶话会 第三期01:选手知多少
2021/03/11 DOTA
python中的__init__ 、__new__、__call__小结
2014/04/25 Python
Python提示[Errno 32]Broken pipe导致线程crash错误解决方法
2014/11/19 Python
利用Celery实现Django博客PV统计功能详解
2017/05/08 Python
Python判断一个文件夹内哪些文件是图片的实例
2018/12/07 Python
python实现Virginia无密钥解密
2019/03/20 Python
使用python搭建服务器并实现Android端与之通信的方法
2019/06/28 Python
详细介绍pandas的DataFrame的append方法使用
2019/07/31 Python
利用python Selenium实现自动登陆京东签到领金币功能
2019/10/31 Python
解决django FileFIELD的编码问题
2020/03/30 Python
Roxy美国官网:澳大利亚冲浪、滑雪健身品牌
2016/07/30 全球购物
如何实现jdbc性能优化
2012/07/30 面试题
27个经典Linux面试题及答案,你知道几个?
2014/03/11 面试题
医院实习接收函
2014/01/12 职场文书
开业庆典答谢词
2014/01/18 职场文书
毕业生自荐信格式
2014/03/07 职场文书
80后职场人的职业生涯规划
2014/03/08 职场文书
2015年元旦演讲稿
2014/09/12 职场文书
护士年终考核评语
2014/12/31 职场文书
服务承诺书
2015/01/19 职场文书
老干部座谈会主持词
2015/07/03 职场文书