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

use*_*652 572 python string append

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

var1 = "foo"
var2 = "bar"
var3 = var1 + var2
Run Code Online (Sandbox Code Playgroud)

有没有什么好的内置方法可供使用?

Joh*_*ooy 587

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

最终结果是操作是摊销O(n).

例如

s = ""
for i in range(n):
    s+=str(i)
Run Code Online (Sandbox Code Playgroud)

曾经是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;
}
Run Code Online (Sandbox Code Playgroud)

通过经验验证很容易.

$ 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 3.x? (14认同)
  • @Steve,不,它至少在2.6甚至2.5 (9认同)
  • 不要使用这个。Pep8 明确指出:[代码的编写方式应该不会对 Python 的其他实现(PyPy、Jython、IronPython、Cython、Psyco 等)造成不利影响](https://www.python.org/dev/peps/pep -0008/#programming-recommendations),然后给出这个具体的例子作为要避免的东西,因为它是如此脆弱。最好使用 `"".join(str_a, str_b)` (9认同)
  • 你引用了`PyString_ConcatAndDel`函数但是包含了`_PyString_Resize`的注释.此外,评论并未真正确定您对Big-O的主张 (8认同)
  • 祝贺您利用了 CPython 功能,该功能将使代码在其他实现上爬行。不好的建议。 (4认同)
  • @JohnLaRooy 您可能过早地停止了一次迭代的 CPython 实验。我粗略地将时间因子 10 提高到 1000000,但是从 1000000 到 10000000,它突然需要 **100** 倍的时间。也许它只优化了一定的大小?我在 Windows 10 64 位上运行 Python 2.7.11。 (2认同)

Joh*_*ica 277

不要过早优化.如果你没有理由相信有致的字符串连接的速度瓶颈,那么就坚持++=:

s  = 'foo'
s += 'bar'
s += 'baz'
Run Code Online (Sandbox Code Playgroud)

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

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

s = ''.join(l)
Run Code Online (Sandbox Code Playgroud)

  • @Richo使用.join更有效率.原因是Python字符串是不可变的,因此重复使用s + = more将分配大量连续更大的字符串..join将从其组成部分一次性生成最终字符串. (25认同)
  • @Ben,这个领域有了很大的改进 - 请参阅我的回答 (5认同)

Win*_*ert 38

别.

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

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

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

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

  • 我很抱歉没有比第一个例子更容易阅读(字符串+字符串),第二个例子可能更有效,但不是更易读 (51认同)
  • @ExceptionSlayer,string + string非常容易理解.但是``<div class ='"+ className +"'id ='"+ generateUniqueId()+"'>"+ message_text +"</ div>"`,我发现你的可读性和容易出错,然后``< div class ='{classname}'id ='{id}'> {message_text} </ div>".format(classname = class_name,message_text = message_text,id = generateUniqueId())` (23认同)
  • 在 Python 3.6 中,我们有 `f"&lt;div class='{class_name}' id='{generateUniqueId()}'&gt;{message_text}&lt;/div&gt;"` (3认同)
  • 在这种情况下,该问题的答案是“不,因为该方法不涵盖我的用例” (2认同)

Raf*_*ler 37

str1 = "Hello"
str2 = "World"
newstr = " ".join((str1, str2))
Run Code Online (Sandbox Code Playgroud)

这将str1和str2与空格连接为分隔符.你也可以"".join(str1, str2, ...).str.join()采用可迭代的,所以你必须把字符串放在列表或元组中.

这与内置方法一样高效.


Lau*_*ves 10

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

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


Ram*_*amy 9

这真的取决于你的应用程序.如果你循环遍历数百个单词并希望将它们全部附加到列表中,.join()那就更好了.但是,如果你整理一个长句,你最好不要使用+=.


Tre*_*ton 7

Python 3.6为我们提供了f字符串,这很令人高兴:

var1 = "foo"
var2 = "bar"
var3 = f"{var1}{var2}"
print(var3)                       # prints foobar
Run Code Online (Sandbox Code Playgroud)

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

print(f"1 + 1 == {1 + 1}")        # prints 1 + 1 == 2
Run Code Online (Sandbox Code Playgroud)


Sai*_*i N 6

使用add函数附加字符串:

str1 = "Hello"
str2 = " World"
str3 = str1.__add__(str2)
print(str3)
Run Code Online (Sandbox Code Playgroud)

输出:

Hello World
Run Code Online (Sandbox Code Playgroud)

  • `str + str2` 仍然更短。 (10认同)

ost*_*ach 5

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


清单

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

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'
Run Code Online (Sandbox Code Playgroud)

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 最佳

  • 我想这取决于。我在Python2.7上分别得到`1.19 s`和`992 ms` (2认同)