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 相关文章推荐
Python3中的2to3转换工具使用示例
Jun 12 Python
Python实现的快速排序算法详解
Aug 01 Python
python实现mysql的读写分离及负载均衡
Feb 04 Python
Python多重继承的方法解析执行顺序实例分析
May 26 Python
基于Python中求和函数sum的用法详解
Jun 28 Python
浅析Python 3 字符串中的 STR 和 Bytes 有什么区别
Oct 14 Python
python实现支付宝转账接口
May 07 Python
详解Python3 pandas.merge用法
Sep 05 Python
python集合常见运算案例解析
Oct 17 Python
python二分法查找算法实现方法【递归与非递归】
Dec 06 Python
TensorFlow实现保存训练模型为pd文件并恢复
Feb 06 Python
如何表示python中的相对路径
Jul 08 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判断表单复选框选中状态完整例子
2014/06/24 PHP
php使用标签替换的方式生成静态页面
2015/05/21 PHP
php使用curl打开https网站的方法
2015/06/17 PHP
PHP+jquery+CSS制作头像登录窗(仿QQ登陆)
2016/10/20 PHP
PHP实现绘制二叉树图形显示功能详解【包括二叉搜索树、平衡树及红黑树】
2017/11/16 PHP
TP5框架实现一次选择多张图片并预览的方法示例
2020/04/04 PHP
客户端js判断文件类型和文件大小即限制上传大小
2013/11/20 Javascript
浅谈JavaScript中定义变量时有无var声明的区别
2014/08/18 Javascript
详解AngularJS的通信机制
2015/06/18 Javascript
javascript排序函数实现数字排序
2015/06/26 Javascript
jquery+ajax+text文本框实现智能提示完整实例
2016/07/09 Javascript
js检测离开或刷新页面时表单数据是否更改的方法
2016/08/02 Javascript
基于Vue的ajax公共方法(详解)
2018/01/20 Javascript
babel之配置文件.babelrc入门详解
2018/02/22 Javascript
微信小程序scroll-x失效的完美解决方法
2018/07/18 Javascript
性能优化篇之Webpack构建速度优化的建议
2019/04/03 Javascript
vue实现吸顶、锚点和滚动高亮按钮效果
2019/10/21 Javascript
Python实现基于权重的随机数2种方法
2015/04/28 Python
Python获取Redis所有Key以及内容的方法
2019/02/19 Python
Python实现最常见加密方式详解
2019/07/13 Python
HTML5 Canvas如何实现纹理填充与描边(Fill And Stroke)
2013/07/15 HTML / CSS
国际花店:Pickup Flowers
2020/04/10 全球购物
康拓普公司Java笔面试
2016/09/23 面试题
事业单位接收函
2014/01/10 职场文书
职业生涯规划书前言
2014/04/15 职场文书
房产转让协议书(2014版)
2014/09/30 职场文书
群众路线教育实践活动学习笔记
2014/11/05 职场文书
2014年学生资助工作总结
2014/12/18 职场文书
电影建党伟业观后感
2015/06/01 职场文书
Python入门之使用pandas分析excel数据
2021/05/12 Python
Java中常用解析工具jackson及fastjson的使用
2021/06/28 Java/Android
python数据可视化使用pyfinance分析证券收益示例详解
2021/11/20 Python
Python实现制作销售数据可视化看板详解
2021/11/27 Python
Python+tkinter实现高清图片保存
2022/03/13 Python
Python实现批量将文件复制到新的目录中再修改名称
2022/04/12 Python
Elasticsearch 索引操作和增删改查
2022/04/19 Python