如何在 Python 中将一个字符串附加到另一个字符串?
- 2025-01-07 08:44:00
- admin 原创
- 96
问题描述:
如何有效地将一个字符串附加到另一个字符串?有没有更快的替代方案:
var1 = "foo"
var2 = "bar"
var3 = var1 + var2
要处理列表中的多个字符串,请参阅如何将列表中的项目连接(连接)为单个字符串。
如果某些输入不是字符串,请参阅如何将变量的值放入字符串中(将其插入到字符串中)?但结果仍然应该是字符串。
解决方案 1:
如果您只有一个对字符串的引用,并且将另一个字符串连接到末尾,则 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 byte (newsize
does *not* include that), and a trailing 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] = ' ';
sv->ob_shash = -1; /* invalidate cached hash value */
return 0;
}
通过经验来验证是相当容易的。
$ python -m timeit -s“s =''” “对于 xrange(10) 中的 i:s + ='a'”
1000000 次循环,3 次中最佳:每次循环 1.85 微秒
$ python -m timeit -s“s =''” “对于 xrange(100) 中的 i:s + ='a'”
10000 次循环,3 次中最佳:每次循环 16.8 微秒
$ python -m timeit -s“s =''”“对于i在xrange(1000)中:s + ='a'”
10000 次循环,3 次中最佳:每次循环 158 微秒
$ python -m timeit -s“s =''” “对于 xrange(10000) 中的 i:s + ='a'”
1000 次循环,3 次中最佳:每次循环 1.71 毫秒
$ python -m timeit -s“s =''” “对于 xrange(100000) 中的 i:s + ='a'”
10 次循环,3 次最佳:每次循环 14.6 毫秒
$ python -m timeit -s“s =''” “对于 xrange 中的 i(1000000):s + ='a'”
10 次循环,3 次最佳:每次循环 173 毫秒
但需要注意的是,这种优化不是 Python 规范的一部分。据我所知,它只存在于 cPython 实现中。例如,在 pypy 或 jython 上进行的相同经验测试可能会显示较旧的 O(n**2) 性能。
$ pypy -m timeit -s“s=''” “for i in xrange(10):s+='a'”
10000 次循环,3 次中最佳:每次循环 90.8 微秒
$ pypy -m timeit -s“s=''” “for i in xrange(100):s+='a'”
1000 次循环,3 次中最佳:每次循环 896 微秒
$ pypy -m timeit -s“s=''” “for i in xrange(1000):s+='a'”
100 次循环,3 次最佳:每次循环 9.03 毫秒
$ pypy -m timeit -s“s=''” “for i in xrange(10000):s+='a'”
10 次循环,3 次最佳:每次循环 89.5 毫秒
到目前为止一切都很好,但是,
$ pypy -m timeit -s“s=''” “for i in xrange(100000):s+='a'”
10 圈,3 圈最佳:每圈 12.8 秒
哎呀,甚至比二次方还糟糕。所以 pypy 正在做一些对短字符串很有效,但对较长的字符串表现不佳的事情。
解决方案 2:
不要过早优化。如果你没有理由相信字符串连接会导致速度瓶颈,那么就坚持使用+
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)
解决方案 3:
str1 = "Hello"
str2 = "World"
newstr = " ".join((str1, str2))
这将 str1 和 str2 连接起来,并以空格作为分隔符。您也可以这样做"".join(str1, str2, ...)
。str.join()
采用可迭代对象,因此您必须将字符串放在列表或元组中。
这对于内置方法来说几乎是最高效的。
解决方案 4:
不。
也就是说,大多数情况下,您最好一次生成整个字符串,而不是将其附加到现有字符串。
例如,不要做:obj1.name + ":" + str(obj1.count)
相反:使用"%s:%d" % (obj1.name, obj1.count)
这将更易于阅读并且更加高效。
解决方案 5:
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
解决方案 6:
如果要附加两个字符串,则只需使用+
。
如果需要执行许多附加操作来构建大型字符串,则可以使用StringIO。该接口就像一个文件:您write
可以将文本附加到其中,然后使用它getvalue()
来获取组合的字符串。
解决方案 7:
这确实取决于你的应用程序。如果你要循环数百个单词并希望将它们全部添加到列表中,.join()
则更好。但如果你要组合一个长句子,最好使用+=
。
解决方案 8:
基本上没有区别。唯一一致的趋势是 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秒
Python 3.4
1 圈,3 局最佳:每圈7.99秒
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秒
解决方案 9:
使用添加函数附加字符串:
str1 = "Hello"
str2 = " World"
str3 = str1.__add__(str2)
print(str3)
输出:
Hello World
解决方案 10:
a='foo'
b='baaz'
a.__add__(b)
out: 'foobaaz'
解决方案 11:
另一个选择是使用 .format ,如下所示:
print("{}{}".format(var1, var2))
解决方案 12:
取决于您要做什么。如果您要将变量格式化为要打印的字符串,例如您希望输出为:
Hello, Bob
鉴于名字 Bob,您会希望我们%s. print("Hello, %s" % my_variable)
它很高效,并且适用于所有数据类型(因此您不必str(my_variable)
像对待那样做"a" + str(5)
)。
解决方案 13:
您可以使用它来连接字符串:f“{var1} {var2}”