什么是在python中最有效的string连接方法?

Python中是否有任何有效的批量string连接方法(如C#中的StringBuilder或Java中的StringBuffer )? 我在这里find以下方法:

  • 简单的连接使用+
  • 使用string列表和join方法
  • MutableString模块使用UserString
  • 使用字符数组和array模块
  • 使用来自StringIO模块的StringIO

但是,您的专家使用或build议,为什么?

[ 这里有一个相关的问题 ]

你可能会对这个感兴趣:Guido的一个优化轶事 。 虽然还值得记住,这是一个旧的文章,它早于''.join (尽pipe我猜string.joinfields是或多或less是相同的)

就这一点而言,如果你可以把你的问题解决,那么array模块可能是最快的。 但是''.join可能足够快,并且具有惯用的优点,从而使其他Python程序员更容易理解。

最后,优化的黄金法则是:除非你知道你需要,而不是进行优化,而不是猜测。

您可以使用timeit模块来测量不同的方法。 这可以告诉你哪个是最快的,而不是在互联网上随机的陌生人猜测。

''.join(sequenceofstrings)通常是最好的 – 最简单和最快的。

这取决于你在做什么。

在Python 2.5之后,使用+运算符的string连接速度非常快。 如果只是串联一些值,使用+运算符效果最好:

 >>> x = timeit.Timer(stmt="'a' + 'b'") >>> x.timeit() 0.039999961853027344 >>> x = timeit.Timer(stmt="''.join(['a', 'b'])") >>> x.timeit() 0.76200008392333984 

但是,如果您将一个string放在一个循环中,最好使用列表连接方法:

 >>> join_stmt = """ ... joined_str = '' ... for i in xrange(100000): ... joined_str += str(i) ... """ >>> x = timeit.Timer(join_stmt) >>> x.timeit(100) 13.278000116348267 >>> list_stmt = """ ... str_list = [] ... for i in xrange(100000): ... str_list.append(str(i)) ... ''.join(str_list) ... """ >>> x = timeit.Timer(list_stmt) >>> x.timeit(100) 12.401000022888184 

但是请注意,在区别变得明显之前,您必须将相对较多的string放在一起。

Python 3.6使用Literal String Interpolation改变了已知组件的string 。

鉴于从mkoistinen的答案 ,有string的testing案例

 domain = 'some_really_long_example.com' lang = 'en' path = 'some/really/long/path/' 

竞争者是

  • f'http://{domain}/{lang}/{path}'0.151μs

  • 'http://%s/%s/%s' % (domain, lang, path) – 0.321μs

  • 'http://' + domain + '/' + lang + '/' + path – 0.356μs

  • ''.join(('http://', domain, '/', lang, '/', path))0.249μs (注意构build常量元组的速度比构build常量列表稍快)。

因此目前最短,最漂亮的代码也是最快的。

在Python 3.6的alpha版本中,对于string的实现是最慢的 – 实际上,生成的字节代码与''.join()情况非常相似,不需要调用str.__format__ , 。 这些低效率是在3.6决赛之前解决的。

速度可以与Python 2中最快的方法(在我的计算机上是串联的)相对比; 8位string需要0.203μs ,如果string都是Unicode,则需要0.259μs

按照John Fouhy的回答,除非必须,否则不要优化,但是如果你在这里问这个问题,可能正是因为你必须这样做 。 就我而言,我需要从stringvariables快速组装一些URL。 我注意到没有人(到目前为止)似乎正在考虑string格式的方法,所以我想我会尝试,而且主要是为了温和的兴趣,我想我会扔在那里的string插值运算符好测量。 说实话,我并不认为这两者中的任何一个都会直接进行“+”操作或“.join()”操作。 但猜猜怎么样? 在我的Python 2.7.5系统上, string插值运算符将它们全部统一起来,而string.format()是最糟糕的表演者:

 # concatenate_test.py from __future__ import print_function import timeit domain = 'some_really_long_example.com' lang = 'en' path = 'some/really/long/path/' iterations = 1000000 def meth_plus(): '''Using + operator''' return 'http://' + domain + '/' + lang + '/' + path def meth_join(): '''Using ''.join()''' return ''.join(['http://', domain, '/', lang, '/', path]) def meth_form(): '''Using string.format''' return 'http://{0}/{1}/{2}'.format(domain, lang, path) def meth_intp(): '''Using string interpolation''' return 'http://%s/%s/%s' % (domain, lang, path) plus = timeit.Timer(stmt="meth_plus()", setup="from __main__ import meth_plus") join = timeit.Timer(stmt="meth_join()", setup="from __main__ import meth_join") form = timeit.Timer(stmt="meth_form()", setup="from __main__ import meth_form") intp = timeit.Timer(stmt="meth_intp()", setup="from __main__ import meth_intp") plus.val = plus.timeit(iterations) join.val = join.timeit(iterations) form.val = form.timeit(iterations) intp.val = intp.timeit(iterations) min_val = min([plus.val, join.val, form.val, intp.val]) print('plus %0.12f (%0.2f%% as fast)' % (plus.val, (100 * min_val / plus.val), )) print('join %0.12f (%0.2f%% as fast)' % (join.val, (100 * min_val / join.val), )) print('form %0.12f (%0.2f%% as fast)' % (form.val, (100 * min_val / form.val), )) print('intp %0.12f (%0.2f%% as fast)' % (intp.val, (100 * min_val / intp.val), )) 

结果:

 # python2.7 concatenate_test.py plus 0.360787868500 (90.81% as fast) join 0.452811956406 (72.36% as fast) form 0.502608060837 (65.19% as fast) intp 0.327636957169 (100.00% as fast) 

如果我使用较短的域和较短的path,插值仍然胜出。 不过,这个差别更明显,而且弦长更长。

现在我有一个很好的testing脚本,我也在Python 2.6,3.3和3.4下testing,结果如下。 在Python 2.6中,加号运算符是最快的! 在Python 3上,join胜出。 注意:这些testing在我的系统上是非常重复的。 因此,2.6上的“plus”总是更快,2.7上的“intp”总是更快,而Python 3.x上的“join”总是更快。

 # python2.6 concatenate_test.py plus 0.338213920593 (100.00% as fast) join 0.427221059799 (79.17% as fast) form 0.515371084213 (65.63% as fast) intp 0.378169059753 (89.43% as fast) # python3.3 concatenate_test.py plus 0.409130576998 (89.20% as fast) join 0.364938726001 (100.00% as fast) form 0.621366866995 (58.73% as fast) intp 0.419064424001 (87.08% as fast) # python3.4 concatenate_test.py plus 0.481188605998 (85.14% as fast) join 0.409673971997 (100.00% as fast) form 0.652010936996 (62.83% as fast) intp 0.460400978001 (88.98% as fast) # python3.5 concatenate_test.py plus 0.417167026084 (93.47% as fast) join 0.389929617057 (100.00% as fast) form 0.595661019906 (65.46% as fast) intp 0.404455224983 (96.41% as fast) 

学过的知识:

  • 有时候,我的假设是错误的。
  • testing系统环境。 你会在生产中运行。
  • string插值还没有死!

TL;博士:

  • 如果您使用2.6,请使用+运算符。
  • 如果您使用2.7,则使用'%'运算符。
  • 如果你使用3.x,使用''.join()。

这个url有不同的方法和一些基准比较:

http://skymind.com/~ocrow/python_string/


请注意:这是基于Python 2.2的2009年之前的一个非常古老的比较,所以在大多数情况下应该被忽略。

它几乎取决于每个新连接后新string的相对大小。 用+运算符,为每个串联创build一个新的string。 如果中间string相对较长,则由于正在存储新的中间string,所以+变得越来越慢。

考虑这种情况:

 from time import time stri='' a='aagsdfghfhdyjddtyjdhmfghmfgsdgsdfgsdfsdfsdfsdfsdfsdfddsksarigqeirnvgsdfsdgfsdfgfg' l=[] #case 1 t=time() for i in range(1000): stri=stri+a+repr(i) print time()-t #case 2 t=time() for i in xrange(1000): l.append(a+repr(i)) z=''.join(l) print time()-t #case 3 t=time() for i in range(1000): stri=stri+repr(i) print time()-t #case 4 t=time() for i in xrange(1000): l.append(repr(i)) z=''.join(l) print time()-t 

结果

1 0.00493192672729

2 0.000509023666382

3 0.00042200088501

4 0.000482797622681

在1和2的情况下,我们添加一个大的string,并且join()执行速度提高了10倍。 在情况3和4中,我们添加一个小string,'+'稍微快一点

我遇到了一个情况,我需要一个不可见的大小的可附加string。 这些是基准testing结果(python 2.7.3):

 $ python -m timeit -s 's=""' 's+="a"' 10000000 loops, best of 3: 0.176 usec per loop $ python -m timeit -s 's=[]' 's.append("a")' 10000000 loops, best of 3: 0.196 usec per loop $ python -m timeit -s 's=""' 's="".join((s,"a"))' 100000 loops, best of 3: 16.9 usec per loop $ python -m timeit -s 's=""' 's="%s%s"%(s,"a")' 100000 loops, best of 3: 19.4 usec per loop 

这似乎表明'+ ='是最快的。 skymind链接的结果已经过时了一些。

(我意识到第二个例子并不完整,最后的列表需要join,但是这只是表明简单地准备列表需要比stringconcat更长的时间)。

一年后,让我们用python 3.4.3testingmkoistinen的答案:

  • 加上0.963564149000(快95.83%)
  • join0.923408469000(快100.00%)
  • forms1.501130934000(快61.51%)
  • intp 1.019677452000(快90.56%)

没有改变。 join仍然是最快的方法。 由于intp可以说是可读性方面的最佳select,不过你可能要使用intp。

受@ JasonBaker基准的启发,这是一个简单的比较10 "abcdefghijklmnopqrstuvxyz"string,显示.join()更快; 即使有这个微小的variables增加:

并置

 >>> x = timeit.Timer(stmt='"abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz"') >>> x.timeit() 0.9828147209324385 

join

 >>> x = timeit.Timer(stmt='"".join(["abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz"])') >>> x.timeit() 0.6114138159765048 

对于一小组 短string (即2或3个不超过几个字符的string), 加号仍然更快。 在Python 2和3中使用mkoistinen的精彩脚本:

 plus 2.679107467004 (100.00% as fast) join 3.653773699996 (73.32% as fast) form 6.594011374000 (40.63% as fast) intp 4.568015249999 (58.65% as fast) 

所以当你的代码进行大量单独的小连接时, 如果速度很关键,plus是首选的方法。