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中mechanize库的简单使用示例
Jan 10 Python
CentOS下使用yum安装python-pip失败的完美解决方法
Aug 16 Python
浅谈python爬虫使用Selenium模拟浏览器行为
Feb 23 Python
python生成1行四列全2矩阵的方法
Aug 04 Python
Python使用reportlab模块生成PDF格式的文档
Mar 11 Python
python制作抖音代码舞
Apr 07 Python
Python 硬币兑换问题
Jul 29 Python
python3 sorted 如何实现自定义排序标准
Mar 12 Python
使用python求斐波那契数列中第n个数的值示例代码
Jul 26 Python
Python爬取英雄联盟MSI直播间弹幕并生成词云图
Jun 01 Python
python实现简单的三子棋游戏
Apr 28 Python
Python OpenGL基本配置方式
May 20 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中关于codeigniter的xmlrpc的类在进行数据交换时的类型问题
2011/07/03 PHP
PHP实现的简单mock json脚本分享
2015/02/10 PHP
php基于双向循环队列实现历史记录的前进后退等功能
2015/08/08 PHP
PC端微信扫码支付成功之后自动跳转php版代码
2017/07/07 PHP
Js基础学习资料
2010/11/23 Javascript
JS图片左右无缝隙滚动的实现(兼容IE,Firefox 遵循W3C标准)
2016/09/23 Javascript
Bootstrap popover用法详解
2016/12/22 Javascript
微信小程序动态显示项目倒计时
2019/06/20 Javascript
koa2 用户注册、登录校验与加盐加密的实现方法
2019/07/22 Javascript
JS秒杀倒计时功能完整实例【使用jQuery3.1.1】
2019/09/03 jQuery
Element 默认勾选表格 toggleRowSelection的实现
2019/09/04 Javascript
详解如何在Javascript和Sass之间共享变量
2019/11/13 Javascript
jquery实现进度条状态展示
2020/03/26 jQuery
Ajax获取node服务器数据的完整步骤
2020/09/20 Javascript
[02:54]辉夜杯主赛事第二日败者组 iG.V赛后采访
2015/12/26 DOTA
搞笑的程序猿:看看你是哪种Python程序员
2015/06/12 Python
python在非root权限下的安装方法
2018/01/23 Python
Python实现PS图像调整之对比度调整功能示例
2018/01/26 Python
PyQt5每天必学之拖放事件
2020/08/27 Python
Django项目中使用JWT的实现代码
2019/11/04 Python
如何修复使用 Python ORM 工具 SQLAlchemy 时的常见陷阱
2019/11/19 Python
opencv python在视屏上截图功能的实现
2020/03/05 Python
英国打印机墨水和碳粉商店:Printerinks
2017/06/30 全球购物
全球最大的户外用品零售商之一:The House
2018/06/12 全球购物
AJAX的全称是什么
2012/11/06 面试题
本科毕业生自我鉴定
2013/11/02 职场文书
预备党员党校学习自我评价分享
2013/11/12 职场文书
房屋转让协议书
2014/04/11 职场文书
理发店策划方案
2014/06/05 职场文书
2014年“世界无车日”活动方案
2014/09/21 职场文书
钱学森观后感
2015/06/04 职场文书
答谢酒会主持词
2015/07/02 职场文书
2016春季运动会通讯稿
2015/07/18 职场文书
2016年小学“我们的节日·中秋节”活动总结
2016/04/05 职场文书
《思路决定出路》读后感3篇
2019/12/11 职场文书
Nginx域名转发https访问的实现
2021/03/31 Servers