Python 的字符串连接与 str.join 相比有多慢?
- 2025-01-13 08:52:00
- admin 原创
- 133
问题描述:
由于我在这个帖子上的回答中的评论,我想知道+=
操作员和之间的速度差异是什么''.join()
那么两者的速度对比如何呢?
解决方案 1:
来自:高效字符串连接
方法 1:
def method1():
out_str = ''
for num in xrange(loop_count):
out_str += 'num'
return out_str
方法 4:
def method4():
str_list = []
for num in xrange(loop_count):
str_list.append('num')
return ''.join(str_list)
现在我意识到它们并不是严格具有代表性的,并且第四种方法在迭代和连接每个项目之前会将其附加到列表中,但这是一个公平的迹象。
字符串连接比连接快得多。
为什么?字符串是不可变的,无法就地更改。要更改一个字符串,需要创建一个新的表示(将两者连接起来)。
解决方案 2:
注意:此基准测试是非正式的,需要重新进行,因为它没有全面展示这些方法在处理实际较长的字符串时的性能。正如 @Mark Amery 在评论中提到的,+=
它并不像使用f
-strings 那样可靠地快速,并且str#join
在实际用例中速度也没有那么慢。
这些指标也可能因为后续 CPython 版本(尤其是 3.11)引入的显著性能改进而过时。
现有的答案写得很好并且经过了研究,但是这里是 Python 3.6 时代的另一个答案,因为现在我们有了文字字符串插值(AKA,f
-strings):
>>> import timeit
>>> timeit.timeit('f\'{"a"}{"b"}{"c"}\'', number=1000000)
0.14618930302094668
>>> timeit.timeit('"".join(["a", "b", "c"])', number=1000000)
0.23334730707574636
>>> timeit.timeit('a = "a"; a += "b"; a += "c"', number=1000000)
0.14985873899422586
测试使用 CPython 3.6.5 在配备 2.3 GHz Intel Core i7 的 2012 Retina MacBook Pro 上进行。
解决方案 3:
我的原始代码是错误的,看来+
连接通常更快(特别是在较新的硬件上使用较新版本的 Python)
具体时间如下:
Iterations: 1,000,000
Windows 7、Core i7 上的 Python 3.3
String of len: 1 took: 0.5710 0.2880 seconds
String of len: 4 took: 0.9480 0.5830 seconds
String of len: 6 took: 1.2770 0.8130 seconds
String of len: 12 took: 2.0610 1.5930 seconds
String of len: 80 took: 10.5140 37.8590 seconds
String of len: 222 took: 27.3400 134.7440 seconds
String of len: 443 took: 52.9640 170.6440 seconds
Windows 7、Core i7 上的 Python 2.7
String of len: 1 took: 0.7190 0.4960 seconds
String of len: 4 took: 1.0660 0.6920 seconds
String of len: 6 took: 1.3300 0.8560 seconds
String of len: 12 took: 1.9980 1.5330 seconds
String of len: 80 took: 9.0520 25.7190 seconds
String of len: 222 took: 23.1620 71.3620 seconds
String of len: 443 took: 44.3620 117.1510 seconds
在 Linux Mint、Python 2.7 和一些较慢的处理器上
String of len: 1 took: 1.8840 1.2990 seconds
String of len: 4 took: 2.8394 1.9663 seconds
String of len: 6 took: 3.5177 2.4162 seconds
String of len: 12 took: 5.5456 4.1695 seconds
String of len: 80 took: 27.8813 19.2180 seconds
String of len: 222 took: 69.5679 55.7790 seconds
String of len: 443 took: 135.6101 153.8212 seconds
代码如下:
from __future__ import print_function
import time
def strcat(string):
newstr = ''
for char in string:
newstr += char
return newstr
def listcat(string):
chars = []
for char in string:
chars.append(char)
return ''.join(chars)
def test(fn, times, *args):
start = time.time()
for x in range(times):
fn(*args)
return "{:>10.4f}".format(time.time() - start)
def testall():
strings = ['a', 'long', 'longer', 'a bit longer',
'''adjkrsn widn fskejwoskemwkoskdfisdfasdfjiz oijewf sdkjjka dsf sdk siasjk dfwijs''',
'''this is a really long string that's so long
it had to be triple quoted and contains lots of
superflous characters for kicks and gigles
@!#(*_#)(*$(*!#@&)(*Exc4x32xffx92x23xDFxDFk^%#$!)%#^(*#''',
'''I needed another long string but this one won't have any new lines or crazy characters in it, I'm just going to type normal characters that I would usually write blah blah blah blah this is some more text hey cool what's crazy is that it looks that the str += is really close to the O(n^2) worst case performance, but it looks more like the other method increases in a perhaps linear scale? I don't know but I think this is enough text I hope.''']
for string in strings:
print("String of len:", len(string), "took:", test(listcat, 1000000, string), test(strcat, 1000000, string), "seconds")
testall()
解决方案 4:
如果我预期的不错,对于一个包含 k 个字符串、总共 n 个字符的列表,连接的时间复杂度应该是 O(nlogk),而经典连接的时间复杂度应该是 O(nk)。
这与合并 k 个排序列表的相对成本相同(有效方法是 O(nlkg),而类似于连接的简单方法是 O(nk) )。
解决方案 5:
如果我从算法的角度来说,如果你选择 [ += ],那么它会生成一个新对象,并且它将是 O(n)2。但是如果你使用 [ .join** ],那么它将是 O(n)。
解决方案 6:
我重写了最后一个答案,您能否就我的测试方式分享一下您的意见?
import time
start1 = time.clock()
for x in range (10000000):
dog1 = ' and '.join(['spam', 'eggs', 'spam', 'spam', 'eggs', 'spam','spam', 'eggs', 'spam', 'spam', 'eggs', 'spam'])
end1 = time.clock()
print("Time to run Joiner = ", end1 - start1, "seconds")
start2 = time.clock()
for x in range (10000000):
dog2 = 'spam'+' and '+'eggs'+' and '+'spam'+' and '+'spam'+' and '+'eggs'+' and '+'spam'+' and '+'spam'+' and '+'eggs'+' and '+'spam'+' and '+'spam'+' and '+'eggs'+' and '+'spam'
end2 = time.clock()
print("Time to run + = ", end2 - start2, "seconds")
注意:此示例使用 Python 3.5 编写,其中 range() 的作用类似于以前的 xrange()
我得到的输出:
Time to run Joiner = 27.086106206103153 seconds
Time to run + = 69.79100515996426 seconds
就我个人而言,我更喜欢''.join([])而不是'Plusser way',因为它更干净,更易读。
解决方案 7:
这就是愚蠢的程序被设计用来测试的:)
使用加号
import time
if __name__ == '__main__':
start = time.clock()
for x in range (1, 10000000):
dog = "a" + "b"
end = time.clock()
print "Time to run Plusser = ", end - start, "seconds"
输出:
Time to run Plusser = 1.16350010965 seconds
现在加入....
import time
if __name__ == '__main__':
start = time.clock()
for x in range (1, 10000000):
dog = "a".join("b")
end = time.clock()
print "Time to run Joiner = ", end - start, "seconds"
输出:
Time to run Joiner = 21.3877386651 seconds
因此,在 Windows 上的 python 2.6 上,我认为 + 比 join 快 18 倍:)
扫码咨询,免费领取项目管理大礼包!