gpt4 book ai didi

python - 为什么字符串的startswith比in慢?

转载 作者:IT老高 更新时间:2023-10-28 20:25:05 24 4
gpt4 key购买 nike

令人惊讶的是,我发现 startswithin 慢:

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

众所周知,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

所以调用 startswith 似乎有一些开销,这使得当字符串很小时它会变慢。

然后我试图弄清楚 startswith 调用的开销是多少。

首先,我使用了一个 f 变量来降低点运算的成本——正如本文 answer 中所提到的。 - 这里我们可以看到 startswith 仍然比较慢:

In [16]: f=s.startswith

In [17]: %timeit f("XYZ")
1000000 loops, best of 3: 270 ns per loop

此外,我测试了空函数调用的成本:

In [18]: def func(a): pass

In [19]: %timeit func("XYZ")
10000000 loops, best of 3: 106 ns per loop

不考虑点操作和函数调用的成本,startswith的时间大约是(270-106)=164ns,但是in操作只需要81.7 ns。 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

最佳答案

正如评论中已经提到的,如果你使用 s.__contains__("XYZ") 你得到的结果更类似于 s.startswith("XYZ") 因为它需要走相同的路线:在字符串对象上查找成员,然后调用函数。这通常有点贵(当然还不够你应该担心)。另一方面,当您在 s 中执行 "XYZ"时,解析器会解释该运算符并可以快捷地访问 __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

因此,比较 s.__contains__("XYZ")s.startswith("XYZ") 会产生更相似的结果,但是对于您的示例字符串 sstartswith 还是会比较慢。

为此,您可以检查两者的实现。有趣的是 contains implementation是它是静态类型的,只是假设参数本身是一个 unicode 对象。所以这是非常有效的。

startswith implementation然而,它是一种“动态” 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);
}

这可能是 startswith 对于 contains 的字符串较慢的一个重要原因,因为它很简单。

关于python - 为什么字符串的startswith比in慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31917372/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com