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 PyTorch参数初始化和Finetune
Feb 11 Python
python numpy元素的区间查找方法
Nov 14 Python
python获取本机所有IP地址的方法
Dec 26 Python
python执行scp命令拷贝文件及文件夹到远程主机的目录方法
Jul 08 Python
Python使用pymysql模块操作mysql增删改查实例分析
Dec 19 Python
Python类中self参数用法详解
Feb 13 Python
Django REST framwork的权限验证实例
Apr 02 Python
python 使用递归的方式实现语义图片分割功能
Jul 16 Python
python调用私有属性的方法总结
Jul 24 Python
python+flask编写一个简单的登录接口
Nov 13 Python
python中requests库+xpath+lxml简单使用
Apr 29 Python
Python中Cookies导出某站用户数据的方法
May 17 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
我的论坛源代码(三)
2006/10/09 PHP
ThinkPHP上使用多说评论插件的方法
2014/10/31 PHP
PHP实现使用DOM将XML数据存入数组的方法示例
2017/09/27 PHP
php原生数据库分页的代码实例
2019/02/18 PHP
JavaScript实现常用二级省市级联下拉列表的方法
2015/03/25 Javascript
AngularJS 面试题集锦
2016/09/06 Javascript
jQuery Mobile和HTML5开发App推广注册页
2016/11/07 Javascript
详解Angular 中 ngOnInit 和 constructor 使用场景
2017/06/22 Javascript
JScript实现表格的简单操作
2017/08/15 Javascript
vue中如何创建多个ueditor实例教程
2017/11/14 Javascript
微信小程序之多列表的显示和隐藏功能【附源码】
2018/08/06 Javascript
d3绘制基本的柱形图的实现代码
2018/12/12 Javascript
微信小程序中插入激励视频广告并获取收益(实例代码)
2019/12/06 Javascript
electron+vue实现div contenteditable截图功能
2020/01/07 Javascript
[02:30]DOTA2放量测试专访海涛:呼吁保护新手玩家
2013/08/26 DOTA
python教程之用py2exe将PY文件转成EXE文件
2014/06/12 Python
分享15个最受欢迎的Python开源框架
2014/07/13 Python
用Python登录Gmail并发送Gmail邮件的教程
2015/04/17 Python
ubuntu环境下python虚拟环境的安装过程
2018/01/07 Python
用Django实现一个可运行的区块链应用
2018/03/08 Python
django基于存储在前端的token用户认证解析
2019/08/06 Python
Python实现投影法分割图像示例(一)
2020/01/17 Python
美国时尚大码女装购物网站:Avenue
2019/05/24 全球购物
捷克家电和家具购物网站:OKAY.cz
2020/07/23 全球购物
应聘编辑职位自荐信范文
2014/01/05 职场文书
《童年的发现》教学反思
2014/02/14 职场文书
中学生演讲稿
2014/04/26 职场文书
五分钟演讲稿
2014/04/30 职场文书
工作骂脏话检讨书
2014/10/05 职场文书
教师工作表现评语
2014/12/31 职场文书
钓鱼岛事件感想
2015/08/11 职场文书
MySQL创建索引需要了解的
2021/04/08 MySQL
Python爬虫基础讲解之请求
2021/05/13 Python
Centos环境下Postgresql 安装配置及环境变量配置技巧
2021/05/18 PostgreSQL
浅谈MySQL 亿级数据分页的优化
2021/06/15 MySQL
Windows11里微软已经将驱动程序安装位置A盘删除
2021/11/21 数码科技