如何在Python中将一个字符串追加到另一个字符串?

751 投票
13 回答
2404977 浏览
提问于 2025-04-16 08:29

我该如何高效地把一个字符串加到另一个字符串后面?有没有比下面这种方法更快的替代方案:

var1 = "foo"
var2 = "bar"
var3 = var1 + var2

如果你想处理多个字符串在一个列表中,可以看看 如何把列表中的项目连接成一个字符串

如果有些输入不是字符串,但你希望最后的结果是字符串,可以查看 如何把变量的值放进字符串里(把它插入到字符串中)

13 个回答

64
str1 = "Hello"
str2 = "World"
newstr = " ".join((str1, str2))

这个方法会把str1和str2用空格连接起来。你也可以用"".join(str1, str2, ...)来实现。str.join()需要一个可迭代的对象,所以你得把字符串放在一个列表或元组里。

这就是内置方法中效率最高的方式了。

366

不要过早地进行优化。如果你没有理由相信字符串连接会导致速度瓶颈,那就继续使用 ++= 来拼接字符串吧:

s  = 'foo'
s += 'bar'
s += 'baz'

不过,如果你想要实现类似Java中StringBuilder的效果,那么在Python中常用的方法是先把要拼接的内容放到一个列表里,最后再用 str.join 把它们连接起来:

l = []
l.append('foo')
l.append('bar')
l.append('baz')

s = ''.join(l)
767

如果你只有一个字符串的引用,然后在它的后面拼接另一个字符串,CPython现在会特别处理这种情况,尝试在原地扩展这个字符串。

最终的结果是,这个操作的时间复杂度是O(n),也就是说,随着字符串长度的增加,所需的时间是线性增长的。

举个例子:

s = ""
for i in range(n):
    s += str(i)

以前这个操作的时间复杂度是O(n^2),但现在变成了O(n)。

更多信息

来自源代码(bytesobject.c):

void
PyBytes_ConcatAndDel(register PyObject **pv, register PyObject *w)
{
    PyBytes_Concat(pv, w);
    Py_XDECREF(w);
}


/* The following function breaks the notion that strings are immutable:
   it changes the size of a string.  We get away with this only if there
   is only one module referencing the object.  You can also think of it
   as creating a new string object and destroying the old one, only
   more efficiently.  In any case, don't use this if the string may
   already be known to some other part of the code...
   Note that if there's not enough memory to resize the string, the original
   string object at *pv is deallocated, *pv is set to NULL, an "out of
   memory" exception is set, and -1 is returned.  Else (on success) 0 is
   returned, and the value in *pv may or may not be the same as on input.
   As always, an extra byte is allocated for a trailing \0 byte (newsize
   does *not* include that), and a trailing \0 byte is stored.
*/

int
_PyBytes_Resize(PyObject **pv, Py_ssize_t newsize)
{
    register PyObject *v;
    register PyBytesObject *sv;
    v = *pv;
    if (!PyBytes_Check(v) || Py_REFCNT(v) != 1 || newsize < 0) {
        *pv = 0;
        Py_DECREF(v);
        PyErr_BadInternalCall();
        return -1;
    }
    /* XXX UNREF/NEWREF interface should be more symmetrical */
    _Py_DEC_REFTOTAL;
    _Py_ForgetReference(v);
    *pv = (PyObject *)
        PyObject_REALLOC((char *)v, PyBytesObject_SIZE + newsize);
    if (*pv == NULL) {
        PyObject_Del(v);
        PyErr_NoMemory();
        return -1;
    }
    _Py_NewReference(*pv);
    sv = (PyBytesObject *) *pv;
    Py_SIZE(sv) = newsize;
    sv->ob_sval[newsize] = '\0';
    sv->ob_shash = -1;          /* invalidate cached hash value */
    return 0;
}

通过实际测试很容易验证这一点。

$ python -m timeit -s"s=''" "for i in xrange(10):s+='a'"
1000000 loops, best of 3: 1.85 usec per loop
$ python -m timeit -s"s=''" "for i in xrange(100):s+='a'"
10000 loops, best of 3: 16.8 usec per loop
$ python -m timeit -s"s=''" "for i in xrange(1000):s+='a'"
10000 loops, best of 3: 158 usec per loop
$ python -m timeit -s"s=''" "for i in xrange(10000):s+='a'"
1000 loops, best of 3: 1.71 msec per loop
$ python -m timeit -s"s=''" "for i in xrange(100000):s+='a'"
10 loops, best of 3: 14.6 msec per loop
$ python -m timeit -s"s=''" "for i in xrange(1000000):s+='a'"
10 loops, best of 3: 173 msec per loop

不过需要注意的是,这种优化并不是Python的标准部分。就我所知,它只在cPython的实现中存在。比如在pypy或jython上进行相同的测试,可能会显示出旧的O(n^2)的性能。

$ pypy -m timeit -s"s=''" "for i in xrange(10):s+='a'"
10000 loops, best of 3: 90.8 usec per loop
$ pypy -m timeit -s"s=''" "for i in xrange(100):s+='a'"
1000 loops, best of 3: 896 usec per loop
$ pypy -m timeit -s"s=''" "for i in xrange(1000):s+='a'"
100 loops, best of 3: 9.03 msec per loop
$ pypy -m timeit -s"s=''" "for i in xrange(10000):s+='a'"
10 loops, best of 3: 89.5 msec per loop

到目前为止都很好,但接下来,

$ pypy -m timeit -s"s=''" "for i in xrange(100000):s+='a'"
10 loops, best of 3: 12.8 sec per loop

哎,甚至比二次方还要糟糕。所以pypy在处理短字符串时表现不错,但对于较长的字符串性能就很差了。

撰写回答