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 相关文章推荐
仅利用30行Python代码来展示X算法
Apr 01 Python
python爬虫实战之最简单的网页爬虫教程
Aug 13 Python
python绘制中国大陆人口热力图
Nov 07 Python
Django JWT Token RestfulAPI用户认证详解
Jan 23 Python
对python生成业务报表的实例详解
Feb 03 Python
详解Python3 对象组合zip()和回退方式*zip
May 15 Python
Python Numpy中数据的常用保存与读取方法
Apr 01 Python
Python读取配置文件(config.ini)以及写入配置文件
Apr 08 Python
基于django micro搭建网站实现加水印功能
May 22 Python
Python代码需要缩进吗
Jul 01 Python
python PIL模块的基本使用
Sep 29 Python
基于Python模拟浏览器发送http请求
Nov 06 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文件缓存类实现代码
2015/10/26 PHP
PHP实现对数组分页处理实例详解
2017/02/07 PHP
PHP简单获取随机数的常用方法小结
2017/06/07 PHP
javascript针对DOM的应用分析(四)
2012/04/15 Javascript
JavaScript高级程序设计(第3版)学习笔记3 js简单数据类型
2012/10/11 Javascript
Raphael一个用于在网页中绘制矢量图形的Javascript库
2013/01/08 Javascript
ExtJS4 Grid改变单元格背景颜色及Column render学习
2013/02/06 Javascript
jQuery实现列表自动循环滚动鼠标悬停时停止滚动
2013/09/06 Javascript
使用jquery animate创建平滑滚动效果(可以是到顶部、到底部或指定地方)
2014/05/27 Javascript
js实现新浪微博首页效果
2015/10/16 Javascript
jQuery给元素添加样式的方法详解
2015/12/30 Javascript
EXT中单击button按钮grid添加一行(光标位置可设置)的实例代码
2016/06/02 Javascript
js获取腾讯视频ID的方法
2016/10/03 Javascript
jQuery ajax动态生成table功能示例
2017/06/14 jQuery
easyui datagrid 表格中操作栏 按钮图标不显示的解决方法
2017/07/27 Javascript
详解vue 组件
2020/06/11 Javascript
javascript读取本地文件和目录方法详解
2020/08/06 Javascript
在Apache服务器上同时运行多个Django程序的方法
2015/07/22 Python
Python 搭建Web站点之Web服务器网关接口
2016/11/06 Python
python 接口返回的json字符串实例
2018/03/27 Python
解决Ubuntu pip 安装 mysql-python包出错的问题
2018/06/11 Python
python中itertools模块zip_longest函数详解
2018/06/12 Python
Python基于多线程实现抓取数据存入数据库的方法
2018/06/22 Python
python模拟实现分发扑克牌
2020/04/22 Python
浅谈Python 函数式编程
2020/06/20 Python
Python中正则表达式对单个字符,多个字符和匹配边界等使用
2021/01/27 Python
python实现按日期归档文件
2021/01/30 Python
html5 postMessage解决跨域、跨窗口消息传递方案
2016/12/20 HTML / CSS
幼儿园教师的考核评语
2014/04/18 职场文书
给妈妈洗脚活动方案
2014/08/16 职场文书
《西门豹》教学反思
2016/02/23 职场文书
Apache Calcite 实现方言转换的代码
2021/04/24 Servers
pytorch常用数据类型所占字节数对照表一览
2021/05/17 Python
python turtle绘图命令及案例
2021/11/23 Python
【海涛dota解说】海涛小满开黑4v5被破两路翻盘潮汐第一视角解说
2022/04/01 DOTA
kubernetes集群搭建Zabbix监控平台的详细过程
2022/07/07 Servers