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 相关文章推荐
wxPython定时器wx.Timer简单应用实例
Jun 03 Python
python抓取网页中图片并保存到本地
Dec 01 Python
详解python单例模式与metaclass
Jan 15 Python
Python的标准模块包json详解
Mar 13 Python
Python的爬虫框架scrapy用21行代码写一个爬虫
Apr 24 Python
Python 2.x如何设置命令执行的超时时间实例
Oct 19 Python
python里使用正则表达式的组嵌套实例详解
Oct 24 Python
Laravel框架表单验证格式化输出的方法
Sep 25 Python
python 实现快速生成连续、随机字母列表
Nov 28 Python
详解Python中的路径问题
Sep 02 Python
浅析python实现动态规划背包问题
Dec 31 Python
Python中request的基本使用解决乱码问题
Apr 12 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学习之PHP运算符
2006/10/09 PHP
PHP4 与 MySQL 数据库操作函数详解
2006/12/06 PHP
php防止SQL注入详解及防范
2013/11/12 PHP
PHP四大安全策略
2014/03/12 PHP
PHP date函数常用时间处理方法
2015/05/11 PHP
老鱼 浅谈javascript面向对象编程
2010/03/04 Javascript
javascript 词法作用域和闭包分析说明
2010/08/12 Javascript
JavaScript面向对象编程入门教程
2014/04/16 Javascript
jQuery 3.0十大新特性最终版发布
2016/07/14 Javascript
js实现右键自定义菜单
2016/12/03 Javascript
JavaScript new对象的四个过程实例浅析
2018/07/31 Javascript
JS加密插件CryptoJS实现的DES加密示例
2018/08/16 Javascript
微信小程序之裁剪图片成圆形的实现代码
2018/10/11 Javascript
小程序云开发初探(小结)
2018/10/24 Javascript
小程序如何支持使用 async/await详解
2019/09/12 Javascript
微信小程序实现禁止分享代码实例
2019/10/19 Javascript
js 图片懒加载的实现
2020/10/21 Javascript
解析Python中的生成器及其与迭代器的差异
2016/06/20 Python
Python通过属性手段实现只允许调用一次的示例讲解
2018/04/21 Python
Python中跳台阶、变态跳台阶与矩形覆盖问题的解决方法
2018/05/19 Python
Python爬虫实现(伪)球迷速成
2018/06/10 Python
pycharm 将python文件打包为exe格式的方法
2019/01/16 Python
python中调试或排错的五种方法示例
2019/09/12 Python
服务器端jupyter notebook映射到本地浏览器的操作
2020/04/14 Python
Python实现Word文档转换Markdown的示例
2020/12/22 Python
奥地利顶级内衣丝袜品牌英国站:Wolford英国
2016/08/29 全球购物
Lookfantastic意大利官网:英国知名美妆购物网站
2019/05/31 全球购物
数据库笔试题
2013/05/09 面试题
项目开发计划书
2014/01/09 职场文书
会议室标语
2014/06/21 职场文书
摄影专业毕业生求职信
2014/08/05 职场文书
公务员中国梦演讲稿
2014/08/19 职场文书
第二批党的群众路线教育实践活动个人整改方案
2014/10/31 职场文书
2015年上半年党建工作总结
2015/03/30 职场文书
社区法制宣传月活动总结
2015/05/07 职场文书
SqlServer: 如何更改表的文件组?(进而改变存储位置)
2021/04/05 SQL Server