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实现父类调用两种方法的不同
Jan 15 Python
python 对key为时间的dict排序方法
Oct 17 Python
使用pandas实现csv/excel sheet互相转换的方法
Dec 10 Python
对Python Class之间函数的调用关系详解
Jan 23 Python
详解Django项目中模板标签及模板的继承与引用(网站中快速布置广告)
Mar 27 Python
详解python之heapq模块及排序操作
Apr 04 Python
把JSON数据格式转换为Python的类对象方法详解(两种方法)
Jun 04 Python
Django时区详解
Jul 24 Python
python-视频分帧&amp;多帧合成视频实例
Dec 10 Python
python 实现将list转成字符串,中间用空格隔开
Dec 25 Python
tensorflow实现tensor中满足某一条件的数值取出组成新的tensor
Jan 04 Python
Python爬虫工具requests-html使用解析
Apr 29 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 5.5 创建和验证哈希最简单的方法详解
2013/11/07 PHP
PHP基于新浪IP库获取IP详细地址的方法
2017/05/04 PHP
OAuth认证协议中的HMACSHA1加密算法(实例)
2017/10/25 PHP
javascript学习笔记(十四) window对象使用介绍
2012/06/20 Javascript
JavaScript在多浏览器下for循环的使用方法
2012/11/07 Javascript
JavaScript中数据结构与算法(三):链表
2015/06/19 Javascript
JavaScript中的this关键字使用详解
2015/08/14 Javascript
JS+CSS实现自动切换的网页滑动门菜单效果代码
2015/09/14 Javascript
jQuery拖拽排序插件制作拖拽排序效果(附源码下载)
2016/02/23 Javascript
node.js实现端口转发
2016/04/14 Javascript
jQuery ajax中使用confirm,确认是否删除的简单实例
2016/06/17 Javascript
JS控件bootstrap suggest plugin使用方法详解
2017/03/25 Javascript
从vue源码看props的用法
2019/01/09 Javascript
postman自定义函数实现 时间函数的思路详解
2019/04/17 Javascript
微信小程序实现上传word、txt、Excel、PPT等文件功能
2019/05/23 Javascript
聊聊鉴权那些事(推荐)
2019/08/22 Javascript
js 图片懒加载的实现
2020/10/21 Javascript
Nuxt的路由配置和参数传递方式
2020/11/06 Javascript
Python 面向对象 成员的访问约束
2008/12/23 Python
Pyspider中给爬虫伪造随机请求头的实例
2018/05/07 Python
用Python获取摄像头并实时控制人脸的实现示例
2019/07/11 Python
详解将Python程序(.py)转换为Windows可执行文件(.exe)
2019/07/19 Python
Pandas —— resample()重采样和asfreq()频度转换方式
2020/02/26 Python
python实现将字符串中的数字提取出来然后求和
2020/04/02 Python
Ratchet 模态框的实现
2020/08/19 HTML / CSS
酒店应聘自荐信
2013/11/09 职场文书
大学生简单自荐信
2013/11/10 职场文书
运动会通讯稿50字
2014/01/30 职场文书
2014年三八妇女节活动方案
2014/02/28 职场文书
五年级学生评语
2014/04/22 职场文书
奥林匹克运动会口号
2014/06/19 职场文书
2014年学生会个人工作总结
2014/11/07 职场文书
幼儿园见习总结
2015/06/23 职场文书
Python的flask接收前台的ajax的post数据和get数据的方法
2021/04/12 Python
基于Go Int转string几种方式性能测试
2021/04/28 Golang
html,css,javascript是怎样变成页面的
2023/05/07 HTML / CSS