我在python中意外地发现了一个表单的操作
string1.join(string2)
可以等效表示为
string2.replace('', string1)[len(string1):-len(string1)]
此外,在使用几个不同大小的输入尝试timeit
之后,这种奇怪的加入方式似乎快了两倍多。
答案 0 :(得分:5)
首先,让我们分解为什么会有效。
>>> string1 = "foo"
>>> string2 = "bar"
>>> string1.join(string2)
'bfooafoor'
这是将string1
放在string2
的每个项目(字符)之间的操作。
所以替换空字符串会产生一些有趣的事情,它将空字符之间的间隔计算为空字符串,因此基本上执行相同的任务,除了在开头和结尾有一个额外的分隔符:
>>> string2.replace('', string1)
'foobfooafoorfoo'
因此,将这些切片产生与str.join()
相同的结果:
>>> string2.replace('', string1)[len(string1):-len(string1)]
'bfooafoor'
显然,这个解决方案比str.join()
更不易读,所以我总是建议不要这样做。 str.join()
也已发展为在所有平台上都高效。替换空字符串可能在某些版本的Python上效率低得多(我不知道是否是这种情况,但它有可能 - 就像在CPython中重复连接相当快,但在其他地方并不一定如此。)< / p>
我甚至在文档中找不到任何暗示替换空字符串的行为应该以这种方式运行的内容,the docs for str.replace()
只是说:
返回字符串的副本,其中所有出现的substring old都替换为new。如果给出了可选参数计数,则仅替换第一次计数出现次数。
我认为我们没有理由认为字母之间的间隙应该算作空字符串的出现(可以说,你可以在字符串中的任何地方放置无限空字符串),因此,依赖于这种行为可能是个坏主意。
此操作也非常罕见 - 将一系列字符串连接在一起更为常见 - 连接字符串的各个字符并不是我个人经常要做的事情。
有趣的是,此x.replace("", y)
似乎是the Python source中的特殊内容:
2347 /* Algorithms for different cases of string replacement */
2348
2349 /* len(self)>=1, from="", len(to)>=1, maxcount>=1 */
2350 Py_LOCAL(PyStringObject *)
2351 replace_interleave(PyStringObject *self,
2352 const char *to_s, Py_ssize_t to_len,
2353 Py_ssize_t maxcount)
2354 {
...
这种特殊的外壳可能会使其表现良好。同样,正如文档中没有提到的那样,这是一个实现细节,并且假设它在其他Python版本中的运行速度很快(或根本没有),那将是一个错误。
答案 1 :(得分:5)
正如Lattyware所提到的,对于空字符串替换,它是一个特殊情况replace_interleave
,它是一个直接循环,其中源和字符串中的替换字符被复制到结果字符串。 Loop被编码为尽可能快。
count = self_len+1;
count -= 1;
Py_MEMCPY(result_s, to_s, to_len);
result_s += to_len;
for (i=0; i<count; i++) {
*result_s++ = *self_s++;
Py_MEMCPY(result_s, to_s, to_len);
result_s += to_len;
}
/* Copy the rest of the original string */
Py_MEMCPY(result_s, self_s, self_len-i);
Join方法也有一个循环,但是有一些改进的地方(由于我没有找到所有方面,因为以下方式编码)以及瓶颈的原因。
char *sep = PyString_AS_STRING(self);
seq = PySequence_Fast(orig, "");
/* Catenate everything. */
p = PyString_AS_STRING(res);
for (i = 0; i < seqlen; ++i) {
size_t n;
item = PySequence_Fast_GET_ITEM(seq, i);
n = PyString_GET_SIZE(item);
Py_MEMCPY(p, PyString_AS_STRING(item), n);
p += n;
if (i < seqlen - 1) {
Py_MEMCPY(p, sep, seplen);
p += seplen;
}
}
正如您在此处所见,在一个循环中
以上三种操作,即使它可能是内嵌的,也会产生相当大的开销。 注意 这也解释了为什么使用List与使用STring相比具有不同的结果,如Blended所观察到的
同时比较两个循环,
前者
最后的注释
编写str.join
时考虑到所有形式的可迭代和序列而不仅仅是字符串,并且没有详细说明,它非常期望广义例程的执行速度可能不如专门的例程快提供特定形式的数据。