Python中判断子串存在的性能比较及分析总结

 更新时间:2019年06月23日 08:40:03   作者:栖迟于一丘  
这篇文章主要给大家总结介绍了Python中判断子串存在的性能比较及分析的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Python具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

起步

对于子串搜索,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中数据库like模糊查询方式

    python中数据库like模糊查询方式

    这篇文章主要介绍了python中数据库like模糊查询方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-03-03
  • python替换文件中的指定行数技巧示例详解

    python替换文件中的指定行数技巧示例详解

    这篇文章主要介绍了python替换文件中的指定行数技巧示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • Pythont特殊语法filter,map,reduce,apply使用方法

    Pythont特殊语法filter,map,reduce,apply使用方法

    这篇文章主要介绍了Pythont特殊语法filter,map,reduce,apply使用方法,需要的朋友可以参考下
    2016-02-02
  • python语法 range() 序列类型range

    python语法 range() 序列类型range

    这篇文章主要介绍了python语法 range() 序列类型range,range是一种序列类型,range类型用于表示不可变的整数序列,下面小编整理了简单内容,需要的小伙伴可以参考一下
    2022-01-01
  • python文本数据相似度的度量

    python文本数据相似度的度量

    这篇文章主要为大家详细介绍了python文本数据相似度的度量,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-03-03
  • python变量赋值机制踩坑记录

    python变量赋值机制踩坑记录

    这篇文章主要介绍了python变量赋值机制踩坑记录,我们都知道python有深拷贝和浅拷贝,但变量赋值又是什么机制呢?这是个容易被忽略却又极易踩坑的点,下面我们来一探究竟,需要的朋友可以参考一下
    2022-02-02
  • pyspark操作hive分区表及.gz.parquet和part-00000文件压缩问题

    pyspark操作hive分区表及.gz.parquet和part-00000文件压缩问题

    这篇文章主要介绍了pyspark操作hive分区表及.gz.parquet和part-00000文件压缩问题,针对问题整理了spark操作hive表的几种方式,需要的朋友可以参考下
    2021-08-08
  • Python爬虫之urllib基础用法教程

    Python爬虫之urllib基础用法教程

    这篇文章主要为大家详细介绍了Python爬虫1.1 urllib基础用法教程,用于对Python爬虫技术进行系列文档讲解,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-10-10
  • python委派生成器的具体方法

    python委派生成器的具体方法

    在本篇内容中小编给大家整理了一篇关于python委派生成器的具体方法内容,有兴趣的朋友们可以学习参考下。
    2022-11-11
  • 浅谈python中统计计数的几种方法和Counter详解

    浅谈python中统计计数的几种方法和Counter详解

    今天小编就为大家分享一篇浅谈python中统计计数的几种方法和Counter详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-11-11

最新评论