Python 替换字符串中的空字符串

Python 替换字符串中的空字符串,python,string,performance,Python,String,Performance,我无意中发现在python中,一个 string1.join(string2) 可以等价地表示为 string2.replace('', string1)[len(string1):-len(string1)] 此外,在使用几个不同大小的输入尝试了timeit之后,这种奇怪的连接方式似乎快了一倍多 为什么join方法要慢一些 这样替换空字符串是安全的/定义良好的吗 因此,首先,让我们来分析一下为什么会这样 >>> string1 = "foo" >>> s

我无意中发现在python中,一个

string1.join(string2)
可以等价地表示为

string2.replace('', string1)[len(string1):-len(string1)]
此外,在使用几个不同大小的输入尝试了
timeit
之后,这种奇怪的连接方式似乎快了一倍多

  • 为什么join方法要慢一些
  • 这样替换空字符串是安全的/定义良好的吗

  • 因此,首先,让我们来分析一下为什么会这样

    >>> string1 = "foo"
    >>> string2 = "bar"
    >>> string1.join(string2)
    'bfooafoor'
    
    这是在
    string2
    的每个项目(字符)之间放置
    string1
    的操作

    因此,替换空字符串可以做一些有趣的事情,它将空字符之间的间隔计算为空字符串,因此基本上执行相同的任务,除了在开始和结束处有一个额外的分隔符:

    >>> string2.replace('', string1)
    'foobfooafoorfoo'
    
    因此,将它们切掉会产生与
    str.join()
    相同的结果:

    显然,这个解决方案的可读性远远低于
    str.join()
    ,因此我一直建议不要使用它
    str.join()
    也被开发为在所有平台上都有效。在某些版本的Python上,替换空字符串的效率可能要低得多(我不知道是否是这样,但这是一种可能性——正如重复连接在CPython中相当快,但在其他地方不一定如此)

    我甚至在文档中找不到任何东西表明这种替换空字符串的行为应该以这种方式运行,只需说:

    返回一个字符串的副本,其中所有出现的子字符串old都替换为new。如果给定了可选参数计数,则仅替换第一次出现的计数

    我看不出有什么理由认为字母之间的间隙应该算作空字符串的出现(可以说,可以在字符串中的任何位置容纳无限个空字符串),因此,依赖这种行为可能是个坏主意

    这种操作也很少见——通常会有一系列字符串连接在一起——连接字符串中的单个字符并不是我个人经常要做的事情

    有趣的是,这个
    x.replace(“,y)
    似乎在以下情况下是特殊情况:


    很可能是这种特殊的套管使其性能良好。同样,由于文档中没有提到,这是一个实现细节,假设它在其他Python版本中能以同样快的速度(或根本不能)工作是一个错误。

    正如Lattyware提到的,对于空字符串替换,它是一个特例,
    replace\u interleave
    ,它是一个直接的循环,源和源字符串中的替换字符将复制到结果字符串中。循环编码为尽可能快

    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);
    
    count=self\u len+1;
    计数-=1;
    Py_MEMCPY(结果、结果、结果);
    结果_s+=至_len;
    
    例如(i=0;i将字符串转换为
    join
    似乎是一个瓶颈。将
    string2
    转换为
    list
    会将时间从
    697
    减少到
    148
    ns。跳过
    x.join(y)
    的一个非常常用用法的事实并不重要?例如,
    ''.join(['1','2','3'])怎么办
    ?这方面存在一些问题,首先是可读性。这对人们来说是可怕的。其次,根据所使用的Python实现,方法的性能可能会有很大的差异(CPython不是唯一的问题),正如mmgp指出的,使用
    join()
    使用字符串作为第一个参数实际上是一种非常罕见的操作。对于大的输入,这个奇怪的方法确实很慢。这是一个很好的见解,但要清楚,这是对CPython的纯分析,而不是一般的Python,所以严重依赖它不是一个好主意。@Lattyware:验证所有实现是一项相当艰巨的任务但是我认为我的**最后一个注释**可以是一个普遍的结论。这是一个关于性能问题的结论,但这里也有其他问题。这是有用的信息,但我建议不要以排他性的方式来看待它。@Lattyware:当然,我不否认这个事实,你的回答正确地解决了这个问题。这个回答是er是你的补充,而不是补充。
    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 {
    ...
    
    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);
    
    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;
        }
    }