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


593

除了以下内容外,我想要一种有效的方法来在Python中将一个字符串附加到另一个字符串。

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

有什么好的内置方法可以使用吗?


8
TL; DR:如果您只是想添加字符串的简单方法,而又不关心效率,则可以:"foo" + "bar" + str(3)
Andrew

Answers:


609

如果只有一个对字符串的引用,并且将另一个字符串连接到末尾,则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 =”“”对于xrange(10):s + ='a'
1000000次循环,每循环3:1.85最佳
$ python -m timeit -s“ s =”“”对于xrange(100):s + ='a'
10000次循环,最佳为3次:每个循环16.8微秒
$ python -m timeit -s“ s =”“”对于xrange(1000)中的我来说:s + ='a'“
10000次循环,最佳为3次:每个循环158微秒
$ python -m timeit -s“ s =”“”对于xrange(10000):s + ='a'
1000次循环,每循环3:1.71毫秒最佳
$ python -m timeit -s“ s =”“”对于xrange(100000):s + ='a'
10个循环,每循环最好3:14.6毫秒
$ python -m timeit -s“ s =”“”对于xrange(1000000):s + ='a'
10个循环,最佳3:每个循环173毫秒

不过,请务必注意,此优化不是Python规范的一部分。据我所知,它仅在cPython实现中。例如,对pypy或jython进行的相同经验测试可能会显示较旧的O(n ** 2)性能。

$ pypy -m timeit -s“ s =”“”对于xrange(10)中的i:s + ='a'“
10000次循环,最好为3:每个循环90.8微秒
$ pypy -m timeit -s“ s =”“”对于xrange(100)中的i:s + ='a'“
1000个循环,每循环3:896最佳
$ pypy -m timeit -s“ s =”“”对于xrange(1000)中的i:s + ='a'“
100个循环,每个循环最好3:9.03毫秒
$ pypy -m timeit -s“ s =”“”对于xrange(10000):s + ='a'
10个循环,最好为3:每个循环89.5毫秒

到目前为止一切顺利,但随后,

$ pypy -m timeit -s“ s =”“”对于xrange(100000):s + ='a'
10次​​循环,每循环3:12.8秒的最佳时间

哎呀,甚至比二次还差。因此,pypy可以在短字符串上做得很好,但是在较大的字符串上却表现不佳。


14
有趣。“现在”是指Python 3.x吗?
史蒂夫·乔阿

10
@Steve,不。它至少是2.6,甚至是2.5
John La Rooy

8
您已引用了该PyString_ConcatAndDel函数,但包含的注释_PyString_Resize。此外,意见并没有真正建立你的关于大O要求
温斯顿·尤尔特

3
祝贺您开发了CPython功能,它将使代码在其他实现上进行爬网。不好的建议。
让·弗朗索瓦·法布尔

4
不要使用这个。Pep8明确声明:代码应以不损害Python其他实现(PyPy,Jython,IronPython,Cython,Psyco等)的方式编写,然后给出此特定示例,以避免因为它很脆弱而应避免使用。"".join(str_a, str_b)
Eraw

287

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

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

就是说,如果您的目标是Java的StringBuilder之类的东西,那么规范的Python习惯用法就是将项目添加到列表中,然后最后str.join将它们全部串联起来:

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

s = ''.join(l)

我不知道将字符串构建为列表然后进行.join()处理对速度有何影响,但是我发现这通常是最干净的方法。我在编写的SQL模板引擎的字符串中使用%s表示法也取得了很大的成功。
richo 2010年

25
@Richo使用.join效率更高。原因是Python字符串是不可变的,因此反复使用s + = more将分配许多连续更大的字符串。.join将从其组成部分一次生成最终字符串。

5
@Ben,这方面已经有了很大的进步-请参阅我的回答
John La Rooy 2010年

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

这将str1和str2加上一个空格作为分隔符。您也可以"".join(str1, str2, ...)str.join()需要迭代,因此您必须将字符串放入列表或元组中。

这与内置方法一样高效。


如果str1是empy,会发生什么情况?会设置空白吗?
尤尔根·K

38

别。

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

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

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

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


54
抱歉,没有比第一个示例更容易阅读的(string + string)了,第二个示例可能更有效,但可读性更强
JqueryToAddNumbers

23
@ExceptionSlayer,字符串+字符串很容易理解。但是"<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())
Winston Ewert 2015年

当我试图做的大致等同于PHP / perl的“ string。= verifydata()”或类似操作时,这根本没有帮助。
Shadur '16

@Shadur,我的意思是您应该再考虑一遍,您是否真的想做一些等效的事情,还是完全不同的方法更好?
Winston Ewert

1
在这种情况下,该问题的答案是“不,因为该方法无法涵盖我的用例”
Shadur

11

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


9

这实际上取决于您的应用程序。如果您要遍历数百个单词并将其全部添加到列表中,.join()那就更好了。但是,如果要把很长的句子放在一起,最好使用+=


5

基本上没有区别。唯一一致的趋势是,每个版本的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 最佳


2
我想这取决于。我分别在Python2.71.19 s992 ms
约翰·拉·

4

__add__函数追加字符串

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

输出量

Hello World

4
str + str2还短。
Nik O'Lai

2
a='foo'
b='baaz'

a.__add__(b)

out: 'foobaaz'

1
代码很不错,但是附带说明会很有帮助。为什么使用此方法而不是此页面上的其他答案?
2015年

11
使用a.__add__(b)与写作相同a+b。当使用+运算符连接字符串时,Python会__add__通过将右侧字符串作为参数来调用左侧字符串上的方法。
艾迪2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.