如何有效地将一个字符串附加到另一个字符串?有没有更快的替代方案:

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

若要处理列表中的多个字符串,请参见如何将列表中的项连接(联接)到单个字符串。

看我如何把一个变量的值在一个字符串(插值到字符串)?如果某些输入不是字符串,但结果仍然应该是字符串。


如果您需要执行许多附加操作来构建一个大字符串,您可以使用StringIO或cStringIO。接口就像一个文件。即:你写东西是为了给它附加文本。

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


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

用一个空格作为分隔符连接str1和str2。你也可以做“”。Join (str1, str2,…)Str.join()接受一个可迭代对象,因此必须将字符串放在列表或元组中。

这和内置方法一样高效。


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

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

也就是说,如果你的目标是像Java的StringBuilder这样的东西,标准的Python习惯用法是将项添加到列表中,然后使用str.join将它们全部连接到末尾:

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

s = ''.join(l)

不喜欢。

也就是说,在大多数情况下,最好一次性生成整个字符串,而不是追加到现有的字符串。

例如,不要这样做:obj1.name + ":" + str(obj1.count)

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

这样更容易阅读,也更有效率。


这取决于你的应用。如果要遍历数百个单词,并希望将它们全部追加到列表中,那么.join()更好。但是如果你要组成一个长句子,你最好使用+=。


如果你只对一个字符串有一个引用,并且你将另一个字符串连接到末尾,CPython现在会对这种情况进行特殊处理,并尝试在适当的地方扩展字符串。

最终结果是这个操作平摊了O(n)。

e.g.

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

以前是O(n²)现在是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在处理短字符串时工作得很好,但在处理大字符串时表现很差。


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


List

%%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秒每圈

Python 3.4

1个循环,3个最好:7.99 s每循环

Python 3.5

1个循环,最好的3个循环:8.48秒

Python 3.6

1个循环,最好的3:9.93秒每循环


字符串

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

Python 2.7:

1个循环,最好的3:7.41秒每循环

Python 3.4

1个循环,最好的3:9.08秒每循环

Python 3.5

1个循环,最好的3:8.82秒每循环

Python 3.6

1个循环,最好的3:9.24秒每循环


a='foo'
b='baaz'

a.__add__(b)

out: 'foobaaz'

使用add函数添加字符串:

str1 = "Hello"
str2 = " World"
str3 = str1.__add__(str2)
print(str3)

输出:

Hello World

Python 3.6为我们提供了f字符串,这是一个令人高兴的东西:

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

你可以在花括号内做任何事情

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

另一种选择是使用.format,如下所示:

print("{}{}".format(var1, var2))

这取决于你想做什么。如果你正在将一个变量格式化为一个字符串来打印,例如,你想要输出如下:

Hello, Bob

给定名称Bob,您将希望我们%s。print("Hello, %s" % my_variable) 它是有效的,它适用于所有数据类型(所以你不需要做str(my_variable)就像你做的"a" + str(5)))。