有什么理由不使用“+”连接两个字符串吗?

2024-04-27 08:25:14 发布

您现在位置:Python中文网/ 问答频道 /正文

Python中常见的反模式是在循环中使用+连接字符串序列。这是不好的,因为Python解释器必须为每次迭代创建一个新的字符串对象,并且最终会占用二次方时间。(CPython的最新版本显然可以在某些情况下优化此功能,但其他实现不能,因此不鼓励程序员依赖此功能。)''.join是正确的方法。

但是,我听说(including here on Stack Overflow)不应该使用+进行字符串连接,而应该始终使用''.join或格式字符串。我不明白如果你只连接两个字符串,为什么会这样。如果我的理解是正确的,那就不需要二次方时间,而且我认为a + b''.join((a, b))'%s%s' % (a, b)更干净、更可读。

使用+连接两个字符串是好的做法吗?还是有什么我不知道的问题?


Tags: 对象方法字符串功能版本here时间模式
3条回答

Plus运算符是连接两个Python字符串的完美解决方案。但是,如果您继续添加两个以上的字符串(n>;25),则可能需要考虑其他问题。

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

+连接两个字符串没有错。确实,它比''.join([a, b])更容易阅读。

您是对的,但是用+连接两个以上的字符串是一个O(n^2)操作(与join的O(n)相比),因此变得效率低下。但是这与使用循环无关。甚至a + b + c + ...也是O(n^2),原因是每次连接都会产生一个新字符串。

CPython2.4及更高版本试图减轻这种情况,但是当连接超过2个字符串时,仍然建议使用join

假设永远不应该使用+进行字符串连接,而应该始终使用'。联接可能是一个神话。确实,使用+创建不可变字符串对象的不必要临时副本,但另一个不常被引用的事实是,在循环中调用join通常会增加function call的开销。让我们以你为例。

创建两个列表,一个来自链接的SO问题,另一个来自更大的虚构

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

让我们创建两个函数UseJoinUsePlus来使用各自的join+功能。

>>> 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. 有关字符串的长度
  2. 没有连接操作。

而在开发过程中脱轨的预成熟优化是邪恶的。

相关问题 更多 >