我正在尝试:
python3 -m timeit -c 'len("".join([str(x) for x in range(0, 999999)]))'
10 loops, best of 3: 330 msec per loop
python3 -m timeit -c 'sum((len(y) for y in [str(x) for x in range(0, 999999)]))
10 loops, best of 3: 439 msec per loop
为什么会这样?有没有更快的方法?
附言假定字符串列表将提前。
请您参考如下方法:
暂时忽略那个相当小的时间差异,您的两种方式在内存中实际上存在巨大差异。
sum((len(y) for y in [str(x) for x in range(0, 999999)]))
这将为每个数字创建一个字符串并将其存储在列表中。然后使用生成器表达式循环遍历该列表并对长度求和。所以基本上每个数字都有一个字符串,一个存储所有字符串的列表,以及一个为长度添加的数字。
len(''.join([str(x) for x in range(0, 999999)]))
这将再次为每个数字创建一个字符串并将其存储在列表中。然后你创建一个包含所有数字的巨大字符串。之后,您调用 length on in(然后是 O(1) 调用)。因此,您没有添加到的数字(同时对长度求和),但您确实有另一个长字符串再次组合了所有其他字符串。
因此,即使速度更快,您也会浪费大量内存,这也可能对以后的性能产生影响。
要改善这一切,您应该考虑永久创建尽可能少的东西。不要使用列表理解,因为这实际上会创建列表;不要使用 str.join 因为它需要一个列表并迭代它两次。
sum(len(str(x)) for x in range(0, 999999)))
现在,这仍然比 len(''.join(…)) 方法慢,但不会有那么多的内存开销。事实上,它一次只会创建一个字符串对象,获取它的长度并将其添加到总和中。然后可以立即收集该字符串。
这仍然很慢的原因是 len 和 str 都需要在生成器内部的每次迭代中查找。为了加快速度,使用 map 只查找它两次。 wim 在评论中提出了一个非常好的建议:
sum(map(len, map(str, range(999999))))
对我来说,这实际上比 len(''.join(…)) 方式执行得更快。我的时间安排结果按我的回答中提到的顺序排列:
62.36836282166257
50.54277449168785
58.24419845897603
40.3403849521618
