来自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 是二次时间吗?
答案 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()