How do I append one string to another in Python?
我想要一种在Python中将一个字符串附加到另一个字符串的有效方法。
1 2 3 | var1 ="foo" var2 ="bar" var3 = var1 + var2 |
有没有好的内置方法可以使用?
如果对一个字符串只有一个引用,并且将另一个字符串连接到末尾,那么cpython现在会遇到这种特殊情况,并尝试就地扩展该字符串。
最终结果是,该操作摊销为o(n)。
例如
1 2 3 | s ="" for i in range(n): s+=str(i) |
。
以前是O(n^2),现在是O(n)。
源代码(bytesobject.c):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | 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; } |
经验性验证很容易。
1 2 3 4 5 6 7 8 9 10 11 12 | $ 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)性能。
1 2 3 4 5 6 7 8 | $ 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 |
。
到目前为止还不错,但是,
1 2 | $ pypy -m timeit -s"s=''""for i in xrange(100000):s+='a'" 10 loops, best of 3: 12.8 sec per loop |
哎哟,比二次还要糟。所以Pypy正在做一些可以很好地处理短字符串的工作,但是对于较大的字符串却表现不佳。
不要过早地优化。如果您没有理由相信字符串连接会导致速度瓶颈,那么只需坚持使用
1 2 3 | s = 'foo' s += 'bar' s += 'baz' |
号
也就是说,如果你的目标是像Java的StrugBu建器一样,标准的Python熟语就是把项目添加到列表中,然后使用EDCOX1,6,在最后连接它们:
1 2 3 4 5 6 | l = [] l.append('foo') l.append('bar') l.append('baz') s = ''.join(l) |
不要。
也就是说,在大多数情况下,最好一次生成整个字符串,而不是附加到现有的字符串。
例如,不要这样做:
相反:使用
这样读起来更容易,效率也更高。
1 2 3 | str1 ="Hello" str2 ="World" newstr ="".join((str1, str2)) |
把str1和str2用空格分隔开。你也可以做
这和内置方法的效率差不多。
如果需要执行许多附加操作来构建一个大字符串,可以使用stringio或cstringio。接口就像一个文件。你要附加文本。
如果只是附加两个字符串,那么只需使用
这取决于您的应用程序。如果你循环使用数百个单词,并想把它们全部附加到一个列表中,那么
python 3.6为我们提供了F字符串,这是一种乐趣:
1 2 3 4 | var1 ="foo" var2 ="bar" var3 = f"{var1}{var2}" print(var3) # prints foobar |
号
你可以在花括号内做任何事
1 | print(f"1 + 1 == {1 + 1}") # prints 1 + 1 == 2 |
号
用uu add_uuu函数附加字符串
1 2 3 4 | str ="Hello" str2 =" World" st = str.__add__(str2) print(st) |
号
产量
1 | Hello World |
号
1 2 3 4 5 6 | a='foo' b='baaz' a.__add__(b) out: 'foobaaz' |
。
基本上没有区别。唯一一致的趋势是,每个版本的python似乎都在变慢…:(
表1 2 3 4 5 | %%timeit x = [] for i in range(100000000): # xrange on Python 2.7 x.append('a') x = ''.join(x) |
。
Python2.7
1 loop, best of 3: 7.34 s per loop
号
Python3.4
1 loop, best of 3: 7.99 s per loop
号
Python3.5
1 loop, best of 3: 8.48 s per loop
号
Python3.6
1 loop, best of 3: 9.93 s per loop
号弦
1 2 3 4 | %%timeit x = '' for i in range(100000000): # xrange on Python 2.7 x += 'a' |
Python2.7:
1 loop, best of 3: 7.41 s per loop
号
Python3.4
1 loop, best of 3: 9.08 s per loop
号
Python3.5
1 loop, best of 3: 8.82 s per loop
号
Python3.6
1 loop, best of 3: 9.24 s per loop
号