使用sum()
进行列表连接是pythonic吗?
>>> sum(([n]*n for n in range(1,5)),[])
[1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
答案 0 :(得分:8)
不,不,实际上是shlemiel the painter algorithm。因为每次它想要连接一个新列表时,它必须从头开始遍历整个列表。 (欲了解更多信息,请阅读Joel的这篇文章: http://www.joelonsoftware.com/articles/fog0000000319.html)
最pythonic方式是使用列表理解:
In [28]: [t for n in range(1,5) for t in [n]*n ]
Out[28]: [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
或itertools.chain
:
In [29]: from itertools import chain
In [32]: list(chain.from_iterable([n]*n for n in range(1,5)))
Out[32]: [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
或者作为基于纯生成器的方法,您可以使用repeat
而不是乘以列表:
In [33]: from itertools import chain, repeat
# In python2.X use xrange instead of range
In [35]: list(chain.from_iterable(repeat(n, n) for n in range(1,5)))
Out[35]: [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
或者,如果你对numpy感兴趣,或者你想要一个超快速的方法,那就是:
In [46]: import numpy as np
In [46]: np.repeat(np.arange(1, 5), np.arange(1, 5))
Out[46]: array([1, 2, 2, 3, 3, 3, 4, 4, 4, 4])
答案 1 :(得分:3)
不,这对于大型列表来说会变得很慢。列表推导是一个更好的选择。
通过列表推导,求和和itertools.chain.from_iterable对时间列表展平的代码:
import time
from itertools import chain
def increasing_count_lists(upper):
yield from ([n]*n for n in range(1,upper))
def printtime(func):
def clocked_func(*args):
t0 = time.perf_counter()
result = func(*args)
elapsed_s = time.perf_counter() - t0
print('{:.4}ms'.format(elapsed_s*1000))
return result
return clocked_func
@printtime
def concat_list_sum(upper):
return sum(increasing_count_lists(upper), [])
@printtime
def concat_list_listcomp(upper):
return [item for sublist in increasing_count_lists(upper) for item in sublist]
@printtime
def concat_list_chain(upper):
return list(chain.from_iterable(increasing_count_lists(upper)))
运行它们:
>>> _ = concat_list_sum(5)
0.03351ms
>>> _ = concat_list_listcomp(5)
0.03034ms
>>> _ = concat_list_chain(5)
0.02717ms
>>> _ = concat_list_sum(50)
0.2373ms
>>> _ = concat_list_listcomp(50)
0.2169ms
>>> _ = concat_list_chain(50)
0.1467ms
>>> _ = concat_list_sum(500)
167.6ms
>>> _ = concat_list_listcomp(500)
8.319ms
>>> _ = concat_list_chain(500)
12.02ms