Python 为什么str.strip()比str.strip快那么多?

Python 为什么str.strip()比str.strip快那么多?,python,string,performance,python-3.x,python-internals,Python,String,Performance,Python 3.x,Python Internals,使用可以通过两种方式在空白处进行拆分。您可以发出不带参数的调用,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 fa

使用可以通过两种方式在空白处进行拆分。您可以发出不带参数的调用,
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
也存在类似的情况。也

python3.5.2
执行计时,在
python2.7.1
上,差异不太明显。没有任何有用的指示,那么,为什么会发生这种情况

在tl中;时尚博士: 这是因为两种不同的情况下存在两种功能,如中所示
do_strip
\u pyunicodestrip
第一个执行速度比第二个快得多

函数用于不存在参数的常见情况
str.strip()
,而(它包装
\u PyUnicode\u XStrip
)用于调用
str.strip(arg)
的情况,即提供参数


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

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

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

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

对于
do_strip
: 在最常见的情况下,要拆分的字符串中的字符可以用
ascii
表示,这会带来额外的性能提升

while (i < len) {
    Py_UCS1 ch = data[i];
    if (!_Py_ascii_whitespace[ch])
        break;
    i++;
}
  • 通过
    Py_UCS4 ch=PyUnicode_读取(种类、数据、i)完成访问
  • 检查字符是否为空白是由宏完成的(该宏仅调用另一个宏:)
对于
\u pyunicodestrip
: 在本例中,访问底层数据与前一例一样,是通过
PyUnicode_Read
完成的;另一方面,检查字符是否是空白(或者实际上是我们提供的任何字符)要稍微复杂一些

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++;
}
while(i
使用,虽然效率很高,但与阵列访问相比要复杂得多,速度也慢得多。对于字符串中的每个字符,将调用它以查看该字符是否包含在我们提供的分隔符中。随着字符串长度的增加,不断调用此函数所带来的开销也会增加

对于那些感兴趣的人来说,
PyUnicode\u FindChar
经过多次检查后,最终将在
stringlib
内部调用,在分隔符长度为
<10
的情况下,它将循环,直到找到字符为止

除此之外,考虑为了到达这里需要调用的附加函数。



至于
lstrip
rstrip
,情况类似。要执行条带化的模式存在的标志,即:
rstrip的
RIGHTSTRIP
lstrip的
LEFTSTRIP
,以及
strip的
BOTHSTRIP
do_strip
\u PyUnicode_XStrip
中的逻辑是根据标志有条件地执行的。

由于@Jims answer中解释的原因,在
字节
对象中发现了相同的行为:

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

b.strip()      # takes 427ns
b.strip(b' ')  # takes 1.2μs
对于
bytearray
对象,这种情况不会发生,在这种情况下执行
split
的函数对于这两种情况都是类似的

此外,在
python2
中,根据我的计时,同样的情况适用于较小的范围

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

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