Python字符串连接成语。需要澄清。

时间:2009-12-28 02:20:20

标签: python performance idioms

来自http://jaynes.colorado.edu/PythonIdioms.html

  

“将字符串构建为列表并使用   ''。最后加入。 join是一个字符串   在分隔符上调用的方法,而不是   列表。从空中调用它   字符串连接片断没有   分隔符,这是一个Python的怪癖和   起初相当令人惊讶。这是   重要的是:用+构建字符串   二次时间而不是线性!如果   你学习了一个成语,学习这个。

     

错误:对于字符串中的s:结果+ = s

     

右:结果=''。join(字符串)“

我不确定为什么这是真的。如果我有一些字符串我想加入它们,对我来说,将它们放入列表然后调用''.join并不是直觉上更好。不将它们放入列表会产生一些开销吗?澄清...

Python命令行:

>>> str1 = 'Not'
>>> str2 = 'Cool'
>>> str3 = ''.join([str1, ' ', str2]) #The more efficient way **A**
>>> print str3
Not Cool
>>> str3 = str1 + ' ' + str2 #The bad way **B**
>>> print str3
Not Cool

A 是非常线性的时间而 B 是二次时间吗?

5 个答案:

答案 0 :(得分:14)

是。对于您选择的示例,重要性不明确,因为您只有两个非常短的字符串,因此附加可能会更快。

但每次在Python中使用字符串a + b时,都会导致新的分配,然后将a和b中的所有字节复制到新字符串中。如果在具有大量字符串的循环中执行此操作,则必须再次复制这些字节,并且每次必须再次复制,并且每次必须复制的数量变得更长。这给出了二次行为。

另一方面,创建字符串列表不会复制字符串的内容 - 它只是复制引用。这非常快,并且在线性时间内运行。然后,join方法只进行一次内存分配,并将每个字符串复制到正确的位置一次。这也只需要线性时间。

所以是的,如果你有可能处理大量的字符串,请使用''.join习语。只需要两个字符串就没关系。

如果您需要更有说服力,请尝试自己创建一个10M字符的字符串:

>>> chars = ['a'] * 10000000
>>> r = ''
>>> for c in chars: r += c
>>> print len(r)

与:

相比
>>> chars = ['a'] * 10000000
>>> r = ''.join(chars)
>>> print len(r)

第一种方法大约需要10秒钟。第二个不到1秒。

答案 1 :(得分:6)

重复串联是二次的,因为它是Schlemiel the Painter's Algorithm(请注意,某些实现会优化它,使其不是二次的)。 join避免了这种情况,因为它占用整个字符串列表,分配必要的空间并在一次传递中进行连接。

答案 2 :(得分:4)

当你编码s1 + s2时,Python需要分配一个新的字符串对象,将s1的所有字符复制到其中,然后在s2之后复制所有字符。这个微不足道的操作不会产生二次时间成本:成本为O(len(s1) + len(s2))(加上一个常量用于分配,但这并不代表大O; - )。

但是,请考虑您提供的报价中的代码:for s in strings: result += s

此处,每次添加新的s时,所有先前的必须首先复制到新分配的{{1}空间}(字符串是不可变的,因此必须进行新的分配和复制)。假设您有N个长度为L的字符串:您将第一次复制L个字符,然后第二次复制2 * L,然后第三次复制3 * L ...总之,这使得result个字符变为复制......所以,是的,它是N的二次方。

在其他一些情况下,对于足够小的N值,二次算法可能比线性算法快(因为乘数和常数固定成本可能要小得多);但事实并非如此,因为分配成本很高(直接和间接因为内存碎片的可能性)。相比之下,将字符串累积到列表中的开销基本上可以忽略不计。

答案 3 :(得分:1)

乔尔在Back to Basics写道。

答案 4 :(得分:0)

如果你指的是与其他人相同的东西,那就不明显了。当您拥有多个字符串时,此优化非常重要,例如长度为 N M 。然后

A

x = ''.join(strings) # Takes M*N operations 

<强>乙

x = ''
for s in strings:
    x = x + s  # Takes N + 2*N + ... + M*N operations

除非通过实施进行优化,否则 A 在总长T = M*N中是线性的, B T*T / N,这总是更糟如果M >> N,则大致为二次方。

现在为什么join实际上非常直观:当你说“我有一些字符串”时,通常可以通过说你有一个返回字符串的迭代器来形式化。现在,这正是您传递给"string".join()

的内容