为什么str.strip()比str.strip('')快得多?

时间:2016-07-09 19:38:31

标签: python string performance python-3.x python-internals

使用 str.strip ,可以通过两种方式分割空白区域。您可以发出不带参数的调用str.strip(),默认情况下使用空格分隔符,或者使用str.strip(' ')自己明确地提供参数。

但是,为什么定时这些功能的表现如此不同?

使用具有有意量的空格的样本字符串:

s = " " * 100 + 'a' + " " * 100

s.strip()s.strip(' ')的时间分别为:

%timeit s.strip()
The slowest run took 32.74 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 396 ns per loop

%timeit s.strip(' ')
100000 loops, best of 3: 4.5 µs per loop

strip需要396nsstrip(' ')需要4.5 μsrsplitlsplit在相同条件下会出现类似情况。另外,bytes objects seem do be affected too

时间是Python 3.5.2 Python 2.7.1,时间{{1}}差异不大。 docs on str.split没有表明任何有用的内容,因此为什么会发生

2 个答案:

答案 0 :(得分:34)

以tl; dr方式:

这是因为两种不同情况存在两种功能,如unicode_strip中所示; do_strip_PyUnicodeXStrip第一次执行的速度比第二次快得多。

功能 do_strip 适用于没有参数的常见案例str.strip()do_argstrip(包裹_PyUnicode_XStrip){调用{1}},即提供参数。

str.strip(arg)只检查分隔符,如果它有效且不等于do_argstrip(在这种情况下调用None),则调用_PyUnicode_XStrip

do_stripdo_strip都遵循相同的逻辑,使用两个计数器,一个等于零,另一个等于字符串的长度。

使用两个_PyUnicode_XStrip循环,第一个计数器递增,直到达到不等于分隔符的值,第二个计数器递减,直到满足相同的条件。

区别在于检查当前字符是否不等于分隔符的执行方式。

对于while

在最常见的情况下,要分割的字符串中的字符可以用do_strip表示,这会带来额外的小性能提升。

ascii
  • 通过访问基础数组,快速访问数据中的当前字符:while (i < len) { Py_UCS1 ch = data[i]; if (!_Py_ascii_whitespace[ch]) break; i++; }
  • 检查字符是否为空格是由一个名为_Py_ascii_whitespace[ch]的数组中的简单数组索引构成的。

因此,简而言之,它非常有效。

如果字符不在Py_UCS1 ch = data[i];范围内,差异不是那么大,但它们确实会降低整体执行速度:

ascii
  • 使用while (i < len) { Py_UCS4 ch = PyUnicode_READ(kind, data, i); if (!Py_UNICODE_ISSPACE(ch)) break; i++; }
  • 进行访问
  • 检查字符是否为空白是由Py_UNICODE_ISSPACE(ch)宏完成的(它只是调用另一个宏:Py_ISSPACE

对于Py_UCS4 ch = PyUnicode_READ(kind, data, i);

对于这种情况,访问基础数据,就像在前一种情况下一样,使用_PyUnicodeXStrip完成;另一方面,检查字符是否为白色空格(或者实际上,我们提供的任何字符)都相当复杂。

PyUnicode_Read
使用

PyUnicode_FindChar,虽然效率很高,但与数组访问相比要复杂得多且速度慢。对于字符串中的每个字符,调用它以查看该字符是否包含在我们提供的分隔符中。随着字符串长度的增加,通过连续调用此函数引入的开销也会增加。

对于那些感兴趣的人,while (i < len) { Py_UCS4 ch = PyUnicode_READ(kind, data, i); if (!BLOOM(sepmask, ch)) break; if (PyUnicode_FindChar(sepobj, ch, 0, seplen, 1) < 0) break; i++; } 经过相当多的检查后,最终会在PyUnicode_FindChar内调用find_char,如果分隔符的长度为stringlib,则会循环到< 10它找到了这个角色。

除此之外,请考虑需要已经调用的附加功能才能到达此处。

对于lstriprstrip,情况类似。存在要执行条带化模式的标志,即:RIGHTSTRIPrstripLEFTSTRIPlstripBOTHSTRIPstripdo_strip_PyUnicode_XStrip内的逻辑基于标志有条件地执行。

答案 1 :(得分:7)

由于@Jims回答中解释的原因,在bytes个对象中找到了相同的行为:

b = bytes(" " * 100 + "a" + " " * 100, encoding='ascii')

b.strip()      # takes 427ns
b.strip(b' ')  # takes 1.2μs

对于bytearray个对象,这种情况不会发生,在这种情况下执行split的函数对于这两种情况都是相似的。

此外,在Python 2中,根据我的时间安排,这同样适用于较小的范围。