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

时间:2010-12-14 01:41:29

标签: python string append

我想要一种有效的方法在Python中将一个字符串附加到另一个字符串。

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

是否有任何好的内置方法可供使用?

10 个答案:

答案 0 :(得分:553)

如果你只有一个字符串的引用,并且你将另一个字符串连接到结尾,那么CPython现在就特殊情况了,并尝试将字符串扩展到位。

最终结果是操作分摊为O(n)。

e.g。

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正在做一些适用于短字符串的东西,但对于较大的字符串表现不佳。

答案 1 :(得分:265)

不要过早优化。如果您没有理由相信字符串连接会导致速度瓶颈,那么只需坚持使用++=

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

也就是说,如果你的目标是像Java的StringBuilder那样,那么规范的Python习惯就是将项添加到列表中,然后使用str.join将它们全部连接起来:

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

s = ''.join(l)

答案 2 :(得分:39)

别。

也就是说,对于大多数情况,你最好一次性生成整个字符串,而不是附加到现有字符串。

例如,请勿执行:obj1.name + ":" + str(obj1.count)

相反:使用"%s:%d" % (obj1.name, obj1.count)

这将更容易阅读和更有效。

答案 3 :(得分:34)

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

以空格作为分隔符连接str1和str2。您也可以"".join(str1, str2, ...)str.join()采用可迭代的方式,因此您必须将字符串放在列表或元组中。

这与内置方法一样高效。

答案 4 :(得分:10)

如果您需要执行许多追加操作来构建大字符串,则可以使用StringIO或cStringIO。界面就像一个文件。即:你write向其附加文字。

如果您只是附加两个字符串,那么只需使用+

答案 5 :(得分:9)

这实际上取决于您的申请。如果您循环遍历数百个单词并希望将它们全部附加到列表中,.join()会更好。但是,如果你要整理一个长句,你最好使用+=

答案 6 :(得分:6)

Python 3.6给了f-strings,这很令人高兴:

var1 = "foo"
var2 = "bar"
var3 = f"{var1}{var2}"
print(var3)                       # prints foobar

您可以在花括号内执行大多数操作

print(f"1 + 1 == {1 + 1}")        # prints 1 + 1 == 2

答案 7 :(得分:3)

基本上没什么区别。唯一一致的趋势是每个版本的Python似乎都变得越来越慢...... :(

列表

%%timeit
x = []
for i in range(100000000):  # xrange on Python 2.7
    x.append('a')
x = ''.join(x)

Python 2.7

  

1循环,最佳3: 7.34 s每循环

Python 3.4

  

1循环,最佳3: 7.99 s每循环

Python 3.5

  

1循环,最佳3: 8.48 s每循环

Python 3.6

  

1循环,最佳3: 9.93 s每循环

字符串

%%timeit
x = ''
for i in range(100000000):  # xrange on Python 2.7
    x += 'a'

Python 2.7

  

1循环,最佳3: 7.41 s 每循环

Python 3.4

  

1循环,最佳3: 9.08 s每循环

Python 3.5

  

1循环,最佳3: 8.82 s每循环

Python 3.6

  

1循环,最佳3: 9.24 s每循环

答案 8 :(得分:3)

a='foo'
b='baaz'

a.__add__(b)

out: 'foobaaz'

答案 9 :(得分:3)

使用 __ add __ 函数附加字符串

str = "Hello"
str2 = " World"
st = str.__add__(str2)
print(st)

输出

Hello World