使用 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
需要396ns
而strip(' ')
需要4.5 μs
,rsplit
和lsplit
在相同条件下会出现类似情况。另外,bytes objects
seem do be affected too。
时间是Python 3.5.2
Python 2.7.1
,时间{{1}}差异不大。 docs on str.split
没有表明任何有用的内容,因此为什么会发生?
答案 0 :(得分:34)
这是因为两种不同情况存在两种功能,如unicode_strip
中所示; do_strip
和_PyUnicodeXStrip
第一次执行的速度比第二次快得多。
功能 do_strip
适用于没有参数的常见案例str.strip()
和do_argstrip
(包裹_PyUnicode_XStrip
){调用{1}},即提供参数。
str.strip(arg)
只检查分隔符,如果它有效且不等于do_argstrip
(在这种情况下调用None
),则调用_PyUnicode_XStrip
。
do_strip
和do_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
它找到了这个角色。
除此之外,请考虑需要已经调用的附加功能才能到达此处。
对于lstrip
和rstrip
,情况类似。存在要执行条带化模式的标志,即:RIGHTSTRIP
为rstrip
,LEFTSTRIP
为lstrip
,BOTHSTRIP
为strip
。 do_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
中,根据我的时间安排,这同样适用于较小的范围。