任何不使用“+”连接两个string的原因?

Python中常见的反模式是在循环中使用+连接string序列。 这很糟糕,因为Python解释器必须为每个迭代创build一个新的string对象,并且最终要花费二次时间。 (在某些情况下,CPython的最新版本显然可以优化这个版本,但其他的实现不能这样做,所以程序员不要依赖这个)。 ''.join是正确的方法。

但是,我听说它( 包括在这里堆栈溢出 ),你永远不应该使用+string连接,而是总是使用''.join或格式string。 我不明白为什么这是如此,如果你只是串联两个string。 如果我的理解是正确的,它不应该花费二次时间,我认为a + b''.join((a, b))'%s%s' % (a, b)

使用+连接两个string是否是一个好习惯? 或者有没有我不知道的问题?

+连接两个string是没有问题的。 确实比''.join([a, b])更容易阅读。

你说得对,虽然连接2个以上的string是O(n ^ 2)操作(与O(n)相比),因此变得效率低下。 但是这与使用循环没有关系。 即使是a + b + c + ...也是O(n ^ 2),原因是每个连接产生一个新的string。

CPython2.4及以上版本试图缓解这一点,但是当连接多于2个string时使用join仍然是可取的。

Plus运算符是连接两个 Pythonstring的完美解决scheme。 但是如果你不断添加两个以上的string(n> 25),你可能想要另外想一想。

''.join([a, b, c])技巧是性能优化。

与多人合作时,有时很难确切知道发生了什么事情。 使用格式string而不是串联可以避免一个特定的烦恼,这是发生在我们整整一吨的时间:

说,一个函数需要一个参数,你写它希望得到一个string:

 In [1]: def foo(zeta): ...: print 'bar: ' + zeta In [2]: foo('bang') bar: bang 

所以,这个函数可能会在整个代码中使用得相当频繁。 你的同事可能确切地知道它做了什么,但不一定是完全按照内部的速度,可能不知道函数期望一个string。 所以他们可能最终会这样做:

 In [3]: foo(23) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) /home/izkata/<ipython console> in <module>() /home/izkata/<ipython console> in foo(zeta) TypeError: cannot concatenate 'str' and 'int' objects 

如果你只是使用一个格式string,那就没有问题了:

 In [1]: def foo(zeta): ...: print 'bar: %s' % zeta ...: ...: In [2]: foo('bang') bar: bang In [3]: foo(23) bar: 23 

定义__str__所有types的对象也是如此,这些对象也可以被传入:

 In [1]: from datetime import date In [2]: zeta = date(2012, 4, 15) In [3]: print 'bar: ' + zeta --------------------------------------------------------------------------- TypeError Traceback (most recent call last) /home/izkata/<ipython console> in <module>() TypeError: cannot concatenate 'str' and 'datetime.date' objects In [4]: print 'bar: %s' % zeta bar: 2012-04-15 

所以是的:如果你可以使用格式化string,那么可以利用Python提供的function。

这个假设永远不会使用string连接,而是总是使用'​​'.join可能是一个神话。 确实,使用+创build不可变的string对象的不必要的临时副本,但另一个经常引用的事实是,在循环中调用join通常会增加function call的开销。 让我们拿你的榜样。

创build两个列表,一个来自链接的SO问题,另一个是更大的伪装

 >>> myl1 = ['A','B','C','D','E','F'] >>> myl2=[chr(random.randint(65,90)) for i in range(0,10000)] 

让我们创build两个函数, UseJoinUsePlus来使用相应的join+function。

 >>> def UsePlus(): return [myl[i] + myl[i + 1] for i in range(0,len(myl), 2)] >>> def UseJoin(): [''.join((myl[i],myl[i + 1])) for i in range(0,len(myl), 2)] 

让我们用第一个列表运行timeit

 >>> myl=myl1 >>> t1=timeit.Timer("UsePlus()","from __main__ import UsePlus") >>> t2=timeit.Timer("UseJoin()","from __main__ import UseJoin") >>> print "%.2f usec/pass" % (1000000 * t1.timeit(number=100000)/100000) 2.48 usec/pass >>> print "%.2f usec/pass" % (1000000 * t2.timeit(number=100000)/100000) 2.61 usec/pass >>> 

他们有几乎相同的运行时间。

让我们使用cProfile

 >>> myl=myl2 >>> cProfile.run("UsePlus()") 5 function calls in 0.001 CPU seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.001 0.001 0.001 0.001 <pyshell#1376>:1(UsePlus) 1 0.000 0.000 0.001 0.001 <string>:1(<module>) 1 0.000 0.000 0.000 0.000 {len} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 1 0.000 0.000 0.000 0.000 {range} >>> cProfile.run("UseJoin()") 5005 function calls in 0.029 CPU seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.015 0.015 0.029 0.029 <pyshell#1388>:1(UseJoin) 1 0.000 0.000 0.029 0.029 <string>:1(<module>) 1 0.000 0.000 0.000 0.000 {len} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 5000 0.014 0.000 0.014 0.000 {method 'join' of 'str' objects} 1 0.000 0.000 0.000 0.000 {range} 

而且看起来使用Join会导致不必要的函数调用,从而增加开销。

现在回到这个问题。 是否应该阻止在所有情况下使用+ join

我相信不,应该考虑的事情

  1. 问题中string的长度
  2. 没有连接操作。

而在开发过程中偏离正轨的优化是邪恶的。

我做了一个快速testing:

 import sys str = e = "a xxxxxxxxxx very xxxxxxxxxx long xxxxxxxxxx string xxxxxxxxxx\n" for i in range(int(sys.argv[1])): str = str + e 

并定时:

 mslade@mickpc:/binks/micks/ruby/tests$ time python /binks/micks/junk/strings.py 8000000 8000000 times real 0m2.165s user 0m1.620s sys 0m0.540s mslade@mickpc:/binks/micks/ruby/tests$ time python /binks/micks/junk/strings.py 16000000 16000000 times real 0m4.360s user 0m3.480s sys 0m0.870s 

显然a = a + b情况的优化。 它不会像人们怀疑的那样performance出O(n ^ 2)的时间。

所以至less在性能方面,使用+很好。

根据Python文档,使用str.join()将为您提供各种Python实现的性能一致性。 虽然CPython优化了s = s + t的二次行为,但其他Python实现可能不会。

CPython实现细节 :如果s和t都是string,那么一些Python实现(如CPython)通常可以对s = s + t或s + = tforms的分配进行就地优化。 在适用的情况下,这种优化使得二次运行时间不太可能。 这个优化是版本和实现相关的。 对于性能敏感的代码,最好使用str.join()方法,以确保跨版本和实现的一致的线性级联性能。

Python文档中的序列types (参见脚注[6])

''.join([a,b])+更好。

因为代码的编写方式不会影响Python的其他实现(PyPy,Jython,IronPython,Cython,Psyco等)

formsa + = b或a = a + b即使在CPython中也是脆弱的,在不使用 refcounting的实现中根本不存在(引用计数是一种将引用数,指针数或句柄存储到资源,如对象,内存块,磁盘空间或其他资源

https://www.python.org/dev/peps/pep-0008/#programming-recommendations