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写入xml文件的方法
May 08 Python
Python3.2中Print函数用法实例详解
May 19 Python
Python获取央视节目单的实现代码
Jul 25 Python
python web框架学习笔记
May 03 Python
Python SVM(支持向量机)实现方法完整示例
Jun 19 Python
python3读取excel文件只提取某些行某些列的值方法
Jul 10 Python
解决Python3 抓取微信账单信息问题
Jul 19 Python
Django CSRF跨站请求伪造防护过程解析
Jul 31 Python
Python3批量移动指定文件到指定文件夹方法示例
Sep 02 Python
python 采用paramiko 远程执行命令及报错解决
Oct 21 Python
python实现跨excel sheet复制代码实例
Mar 03 Python
常用的10个Python实用小技巧
Aug 10 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中的字符串函数
2006/10/09 PHP
WordPress中用于获取文章作者与分类信息的方法整理
2015/12/17 PHP
用PHP的反射实现委托模式的讲解
2019/03/22 PHP
用apply让javascript函数仅执行一次的代码
2010/06/27 Javascript
js中格式化日期时间型数据函数代码
2010/11/08 Javascript
javascript中with()方法的语法格式及使用
2014/08/04 Javascript
js实现jquery的offset()方法实例
2015/01/10 Javascript
详解JavaScript正则表达式中的global属性的使用
2015/06/16 Javascript
JS中创建自定义类型的常用模式总结【工厂模式,构造函数模式,原型模式,动态原型模式等】
2019/01/19 Javascript
原生javascript制作贪吃蛇小游戏的方法分析
2020/02/26 Javascript
小程序实现tab标签页
2020/11/16 Javascript
[02:23]DOTA2英雄基础教程 幻影长矛手
2013/12/09 DOTA
[17:45]DOTA2 HEROES教学视频教你分分钟做大人-军团指挥官
2014/06/11 DOTA
python创建进程fork用法
2015/06/04 Python
Python按行读取文件的简单实现方法
2016/06/22 Python
fastcgi文件读取漏洞之python扫描脚本
2017/04/23 Python
django静态文件加载的方法
2018/05/20 Python
Python递归函数实例讲解
2019/02/27 Python
Django MEDIA的配置及用法详解
2019/07/25 Python
python3下pygame如何实现显示中文
2020/01/11 Python
python序列类型种类详解
2020/02/26 Python
CSS3 display知识详解
2015/11/25 HTML / CSS
HTML5的结构和语义(4):语义性的内联元素
2008/10/17 HTML / CSS
Ellos丹麦:时尚和服装在线
2016/09/19 全球购物
linux比较文件内容的命令是什么
2015/09/23 面试题
办公自动化毕业生求职信
2014/03/09 职场文书
汽车检测与维修专业求职信
2014/07/04 职场文书
人口与计划生育目标管理责任书
2014/07/29 职场文书
房屋租赁合同解除协议书
2014/10/11 职场文书
2014年党建工作汇报材料
2014/10/27 职场文书
医生学习党的群众路线教育实践活动心得体会
2014/11/03 职场文书
新郎接新娘保证书
2015/05/08 职场文书
民间借贷借条范本
2015/05/25 职场文书
毕业班工作总结
2015/08/10 职场文书
抖音动画片,皮皮虾,《治愈系》动画在用这首REMIX作为背景音乐,Anak ,The last world with you完整版
2022/03/16 杂记
mysqldump进行数据备份详解
2022/07/15 MySQL