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根据经纬度计算距离示例
Feb 16 Python
在Django中同时使用多个配置文件的方法
Jul 22 Python
详解Python的Django框架中的中间件
Jul 24 Python
python实现多线程抓取知乎用户
Dec 12 Python
Python(TensorFlow框架)实现手写数字识别系统的方法
May 29 Python
详谈Pandas中iloc和loc以及ix的区别
Jun 08 Python
Python运维开发之psutil库的使用详解
Oct 18 Python
python 判断参数为Nonetype类型或空的实例
Oct 30 Python
Django model update的多种用法介绍
Mar 28 Python
python 中值滤波,椒盐去噪,图片增强实例
Dec 18 Python
python中温度单位转换的实例方法
Dec 27 Python
Pytorch中的数据集划分&正则化方法
May 27 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 计算两个时间戳相隔的时间的函数(小时)
2009/12/18 PHP
php实现可逆加密的方法
2015/08/11 PHP
微信公众平台开发之天气预报功能
2015/08/31 PHP
基于jquery的图片懒加载js
2010/06/30 Javascript
jQuery中的.bind()、.live()和.delegate()之间区别分析
2011/06/08 Javascript
获取offsetTop和offsetLeft值的js代码(兼容)
2013/04/16 Javascript
js获取字符串最后一位方法汇总
2014/11/13 Javascript
js实现正则匹配中文标点符号的方法
2015/12/23 Javascript
基于css3新属性transform及原生js实现鼠标拖动3d立方体旋转
2016/06/12 Javascript
JavaScript每天必学之基础知识
2016/09/17 Javascript
AngularJS实现的回到顶部指令功能实例
2017/05/17 Javascript
解决微信二次分享不显示摘要和图片的问题
2017/08/18 Javascript
vue.js语法及常用指令
2017/10/29 Javascript
vue的全局提示框组件实例代码
2018/02/26 Javascript
AngularJS动态添加数据并删除的实例
2018/02/27 Javascript
vue-cli 引入、配置axios的方法
2018/05/08 Javascript
JavaScript中join()、splice()、slice()和split()函数用法示例
2018/08/24 Javascript
vue-cli history模式实现tomcat部署报404的解决方式
2019/09/06 Javascript
js禁止查看源文件屏蔽Ctrl+u/s、F12、右键等兼容IE火狐chrome
2020/10/01 Javascript
Python记录详细调用堆栈日志的方法
2015/05/05 Python
python使用logging模块发送邮件代码示例
2018/01/18 Python
python redis连接 有序集合去重的代码
2019/08/04 Python
Python基于pandas爬取网页表格数据
2020/05/11 Python
python 实现两个npy档案合并
2020/07/01 Python
css3实现文字首尾衔接跑马灯的示例代码
2020/10/16 HTML / CSS
解决H5的a标签的download属性下载service上的文件出现跨域问题
2019/07/16 HTML / CSS
英文演讲稿开场白
2014/08/25 职场文书
海南召开党的群众路线教育实践活动总结大会新闻稿
2014/10/21 职场文书
春秋淹城导游词
2015/02/11 职场文书
财务经理岗位职责范本
2015/04/08 职场文书
大学运动会加油稿
2015/07/22 职场文书
初中英语教学随笔
2015/08/15 职场文书
导游词之南迦巴瓦峰
2019/11/19 职场文书
Golang 实现超大文件读取的两种方法
2021/04/27 Golang
Anaconda安装pytorch及配置PyCharm 2021环境
2021/06/04 Python
Redis分布式锁的7种实现
2022/04/01 Redis