WKP*_*lus 55 python cpython startswith python-2.7 python-internals
令人惊讶的是,我发现startswith速度比in:
In [10]: s="ABCD"*10
In [11]: %timeit s.startswith("XYZ")
1000000 loops, best of 3: 307 ns per loop
In [12]: %timeit "XYZ" in s
10000000 loops, best of 3: 81.7 ns per loop
Run Code Online (Sandbox Code Playgroud)
众所周知,in操作需要搜索整个字符串,startswith只需要检查前几个字符,所以startswith应该更有效率.
什么时候s足够大,startswith速度更快:
In [13]: s="ABCD"*200
In [14]: %timeit s.startswith("XYZ")
1000000 loops, best of 3: 306 ns per loop
In [15]: %timeit "XYZ" in s
1000000 loops, best of 3: 666 ns per loop
Run Code Online (Sandbox Code Playgroud)
所以看起来调用startswith有一些开销,当字符串很小时它会变慢.
而且我试图弄清楚startswith呼叫的开销是多少.
首先,我使用一个f变量来降低点操作的成本 - 正如本答案中所提到的- 在这里我们可以看到startswith仍然较慢:
In [16]: f=s.startswith
In [17]: %timeit f("XYZ")
1000000 loops, best of 3: 270 ns per loop
Run Code Online (Sandbox Code Playgroud)
此外,我测试了空函数调用的成本:
In [18]: def func(a): pass
In [19]: %timeit func("XYZ")
10000000 loops, best of 3: 106 ns per loop
Run Code Online (Sandbox Code Playgroud)
无论点操作和函数调用的成本如何,时间startswith约为(270-106)= 164ns,但in操作仅需81.7ns.似乎还有一些开销startswith,那是什么?
在poke和lvc 之间startswith和之间添加测试结果__contains__:
In [28]: %timeit s.startswith("XYZ")
1000000 loops, best of 3: 314 ns per loop
In [29]: %timeit s.__contains__("XYZ")
1000000 loops, best of 3: 192 ns per loop
Run Code Online (Sandbox Code Playgroud)
pok*_*oke 38
正如在评论中已经提到的,如果你使用s.__contains__("XYZ")你得到的结果更相似,s.startswith("XYZ")因为它需要采用相同的路径:成员查找字符串对象,然后是函数调用.这通常有些昂贵(当然不足以让你担心).另一方面,当你这样做时"XYZ" in s,解析器会解释运算符,并且可以简化成员访问__contains__(或者更确切地说是它后面的实现,因为__contains__它本身只是访问实现的一种方式).
您可以通过查看字节码来了解这一点:
>>> dis.dis('"XYZ" in s')
1 0 LOAD_CONST 0 ('XYZ')
3 LOAD_NAME 0 (s)
6 COMPARE_OP 6 (in)
9 RETURN_VALUE
>>> dis.dis('s.__contains__("XYZ")')
1 0 LOAD_NAME 0 (s)
3 LOAD_ATTR 1 (__contains__)
6 LOAD_CONST 0 ('XYZ')
9 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
12 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)
所以比较s.__contains__("XYZ")有s.startswith("XYZ")会产生更多类似的结果,但是你的例子字符串s,则startswith仍然会比较慢.
为此,您可以检查两者的实现.有意识地看到contains实现是它是静态类型的,只是假设参数本身是一个unicode对象.所以这非常有效.
然而,startswith实现是一个"动态"Python方法,它要求实现实际解析参数.startswith还支持一个元组作为参数,这使得方法的整个启动速度有点慢:(由我缩短,我的评论):
static PyObject * unicode_startswith(PyObject *self, PyObject *args)
{
// argument parsing
PyObject *subobj;
PyObject *substring;
Py_ssize_t start = 0;
Py_ssize_t end = PY_SSIZE_T_MAX;
int result;
if (!stringlib_parse_args_finds("startswith", args, &subobj, &start, &end))
return NULL;
// tuple handling
if (PyTuple_Check(subobj)) {}
// unicode conversion
substring = PyUnicode_FromObject(subobj);
if (substring == NULL) {}
// actual implementation
result = tailmatch(self, substring, start, end, -1);
Py_DECREF(substring);
if (result == -1)
return NULL;
return PyBool_FromLong(result);
}
Run Code Online (Sandbox Code Playgroud)
这可能是一个很大的原因startswith,contains因为它的简单性使得a 快速的字符串变慢.
| 归档时间: |
|
| 查看次数: |
3336 次 |
| 最近记录: |