繁体   English   中英

如何在 Python 中 append 一个字符串到另一个字符串?

[英]How do I append one string to another in Python?

我如何有效地 append 一个字符串到另一个字符串? 是否有更快的替代方案:

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

如果您只有一个对字符串的引用,并且将另一个字符串连接到末尾,则 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=''" "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 正在做一些对短字符串很有效的事情,但对较大的字符串表现不佳。

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

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

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

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

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

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

这与内置方法的效率差不多。

不要。

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

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

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

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

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

如果你需要做很多追加操作来构建一个大字符串,你可以使用StringIO或 cStringIO。 界面就像一个文件。 即:您write以向其附加文本。

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

这真的取决于你的应用程序。 如果您要遍历数百个单词并希望将它们全部附加到列表中,则.join()更好。 但是如果你把一个长句子放在一起,你最好使用+=

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


列表

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

蟒蛇 2.7

1 个循环,最好的 3 个:每个循环7.34

蟒蛇 3.4

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

蟒蛇 3.5

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

蟒蛇 3.6

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


字符串

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

蟒蛇2.7

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

蟒蛇 3.4

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

蟒蛇 3.5

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

蟒蛇 3.6

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

使用add函数追加字符串

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

输出

Hello World
a='foo'
b='baaz'

a.__add__(b)

out: 'foobaaz'

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

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

取决于你想要做什么。 如果要将变量格式化为要打印的字符串,例如,您希望 output 为:

你好,鲍勃

给定名字 Bob,你会想要我们 %s。 print("Hello, %s" % my_variable) 它很高效,并且适用于所有数据类型(因此您不必像使用 "a" + str(5) 那样执行 str(my_variable))。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM