在Python中连接字符串的首选方法是什么?

在Python中连接字符串的首选方法是什么?,python,string,python-3.x,concat,Python,String,Python 3.x,Concat,由于Python的string无法更改,我想知道如何更有效地连接字符串 我可以这样写: s += stringfromelsewhere 或者像这样: s = [] s.append(somestring) later s = ''.join(s) 在写这个问题的时候,我发现了一篇关于这个话题的好文章 但是它在Python2.x中,所以问题是Python3中有什么变化吗?推荐的方法仍然是使用append和join。如果要连接很多值,那么两者都没有。附加一份清单是昂贵的。您可以使用Str

由于Python的
string
无法更改,我想知道如何更有效地连接字符串

我可以这样写:

s += stringfromelsewhere
或者像这样:

s = []
s.append(somestring)

later

s = ''.join(s)
在写这个问题的时候,我发现了一篇关于这个话题的好文章


但是它在Python2.x中,所以问题是Python3中有什么变化吗?

推荐的方法仍然是使用append和join。

如果要连接很多值,那么两者都没有。附加一份清单是昂贵的。您可以使用StringIO来实现这一点。尤其是当你在大量的操作中建立它的时候

from cStringIO import StringIO
# python3:  from io import StringIO

buf = StringIO()

buf.write('foo')
buf.write('foo')
buf.write('foo')

buf.getvalue()
# 'foofoofoo'
如果您已经从其他操作返回了完整的列表,那么只需使用
'.join(列表)

从python常见问题解答:

str和bytes对象是不可变的,因此连接了许多 字符串在一起效率很低,因为每次连接都会创建一个新的 对象在一般情况下,总运行时成本是平方的 字符串的总长度

要积累许多str对象,推荐的习惯用法是放置它们 进入列表并在末尾调用str.join():

chunks = []
for s in my_strings:
    chunks.append(s)
result = ''.join(chunks)
(另一个相当有效的习惯用法是使用io.StringIO)

要积累多个字节的对象,推荐的习惯用法是扩展 使用就地连接(+=运算符)的bytearray对象:


编辑:我很傻,把结果向后粘贴,让它看起来像是附加到列表中比cStringIO更快。我还添加了bytearray/str-concat的测试,以及使用更大字符串的更大列表的第二轮测试。(python 2.7.3)

ipython大型字符串列表测试示例

try:
    from cStringIO import StringIO
except:
    from io import StringIO

source = ['foo']*1000

%%timeit buf = StringIO()
for i in source:
    buf.write(i)
final = buf.getvalue()
# 1000 loops, best of 3: 1.27 ms per loop

%%timeit out = []
for i in source:
    out.append(i)
final = ''.join(out)
# 1000 loops, best of 3: 9.89 ms per loop

%%timeit out = bytearray()
for i in source:
    out += i
# 10000 loops, best of 3: 98.5 µs per loop

%%timeit out = ""
for i in source:
    out += i
# 10000 loops, best of 3: 161 µs per loop

## Repeat the tests with a larger list, containing
## strings that are bigger than the small string caching 
## done by the Python
source = ['foo']*1000

# cStringIO
# 10 loops, best of 3: 19.2 ms per loop

# list append and join
# 100 loops, best of 3: 144 ms per loop

# bytearray() +=
# 100 loops, best of 3: 3.8 ms per loop

# str() +=
# 100 loops, best of 3: 5.11 ms per loop
虽然有些过时,但建议将
join()
置于
+
之上。在其关于的章节中,有以下免责声明:

本节的准确性在下文中有争议 Python版本。在CPython 2.5中,字符串连接是相当简单的 fast,尽管这可能不适用于其他Python 实现。有关讨论,请参阅ConcatenationTestCode

向字符串变量追加字符串的最佳方法是使用
+
+=
。这是因为它可读性强,速度快。它们的速度也一样快,你选择哪一种取决于你的品味,后者是最常见的。以下是
timeit
模块的计时:

a = a + b:
0.11338996887207031
a += b:
0.11040496826171875
但是,那些建议创建列表并附加到列表中,然后加入这些列表的人这样做是因为将字符串附加到列表中可能比扩展字符串快得多。在某些情况下,这可能是真的。例如,这里有一个例子 一个字符串的百万个附加项,首先是字符串,然后是列表:

a += b:
0.10780501365661621
a.append(b):
0.1123361587524414
好的,即使结果字符串有一百万个字符长,追加速度也更快

现在,让我们尝试将1000个字符长的字符串追加十万次:

a += b:
0.41823482513427734
a.append(b):
0.010656118392944336
因此,结束字符串的长度约为100MB。这相当慢,添加到列表要快得多。该计时不包括最后的
a.join()
。那需要多长时间

a.join(a):
0.43739795684814453
分组。事实证明,即使在这种情况下,append/join速度也较慢

那么,这一建议来自何方?Python 2

a += b:
0.165287017822
a.append(b):
0.0132720470428
a.join(a):
0.114929914474
嗯,如果您使用非常长的字符串(通常不是,那么内存中有一个100MB的字符串会是什么?)

但真正的关键是Python 2.3。我甚至不会告诉你时间,因为它太慢了,还没有结束。这些测试突然需要几分钟。除了append/join,它的速度和后面的python一样快

是的。早在石器时代,Python中的字符串连接非常缓慢。但是在2.4上它不再是了(或者至少是Python2.4.7),所以使用append/join的建议在2008年过时了,当时Python2.3停止更新,您应该停止使用它。:-)

(更新:当我更仔细地进行测试时,发现在Python 2.3上使用
+
+=
对于两个字符串也更快。使用
'.join()
的建议肯定是一个误解)

然而,这是CPython。其他实现可能有其他问题。这就是为什么过早优化是万恶之源的另一个原因。不要使用被认为“更快”的技术,除非你先测量它

因此,进行字符串连接的“最佳”版本是使用+或+=。如果这对你来说太慢了,这是不太可能的,那就做点别的吧


那么为什么我要在代码中使用大量的append/join呢?因为有时候它实际上更清晰。尤其是当您应该连接在一起的内容应该用空格、逗号或换行符分隔时。

使用“+”就地字符串连接是稳定性和交叉实现方面最差的连接方法,因为它不支持所有值。不鼓励这样做,并鼓励长期使用format()、join()和append()

如链接的“规划建议”部分所述:

例如,对于形式为a+=b或a=a+b的语句,不要依赖于CPython的就地字符串连接的高效实现。这种优化即使在CPython中也很脆弱(它只适用于某些类型),并且在不使用refcounting的实现中根本不存在。在库的性能敏感部分中,应改为使用“”。join()形式。这将确保在各种实现中以线性时间进行连接

你也可以使用这个(更有效)。()


如果要连接的字符串是文字,请使用

如果要对字符串的一部分(如上所述)进行注释,或者要对字符串使用或三重引号,则此选项非常有用
a += b:
0.165287017822
a.append(b):
0.0132720470428
a.join(a):
0.114929914474
s += "%s" %(stringfromelsewhere)
re.compile(
        "[A-Za-z_]"       # letter or underscore
        "[A-Za-z0-9_]*"   # letter, digit or underscore
    )
def str_join(*args):
    return ''.join(map(str, args))
str_join('Pine')  # Returns : Pine
str_join('Pine', 'apple')  # Returns : Pineapple
str_join('Pine', 'apple', 3)  # Returns : Pineapple3
query = "insert into {0}({1},{2},{3}) values({4}, {5}, {6})"
query.format('users','name','age','dna','suzan',1010,'nda')
>>> name = 'some_name'
>>> number = 123
>>>
>>> f'Name is {name} and the number is {number}.'
'Name is some_name and the number is 123.'
def test_concat_chunk(seq, split_by):
    result = ['']
    for item in seq:
        if len(result[-1]) + len(item) > split_by: 
            result.append('')
        result[-1] += item
    return result
import io

def test_stringio_chunk(seq, split_by):
    def chunk():
        buf = io.StringIO()
        size = 0
        for item in seq:
            if size + len(item) <= split_by:
                size += buf.write(item)
            else:
                yield buf.getvalue()
                buf = io.StringIO()
                size = buf.write(item)
        if size:
            yield buf.getvalue()

    return list(chunk())

def test_join_chunk(seq, split_by):
    def chunk():
        buf = []
        size = 0
        for item in seq:
            if size + len(item) <= split_by:
                buf.append(item)
                size += len(item)
            else:
                yield ''.join(buf)                
                buf.clear()
                buf.append(item)
                size = len(item)
        if size:
            yield ''.join(buf)

    return list(chunk())
import timeit
import random
import string
import matplotlib.pyplot as plt

line = ''.join(random.choices(
    string.ascii_uppercase + string.digits, k=512)) + '\n'
x = []
y_concat = []
y_stringio = []
y_join = []
n = 5
for i in range(1, 11):
    x.append(i)
    seq = [line] * (20 * 2 ** 20 // len(line))
    chunk_size = i * 2 ** 20
    y_concat.append(
        timeit.timeit(lambda: test_concat_chunk(seq, chunk_size), number=n) / n)
    y_stringio.append(
        timeit.timeit(lambda: test_stringio_chunk(seq, chunk_size), number=n) / n)
    y_join.append(
        timeit.timeit(lambda: test_join_chunk(seq, chunk_size), number=n) / n)
plt.plot(x, y_concat)
plt.plot(x, y_stringio)
plt.plot(x, y_join)
plt.legend(['concat', 'stringio', 'join'], loc='upper left')
plt.show()