以下是代码:
import itertools, time
x = {1:[1, 2], 2:[2, 3], 3:[3, 4]}
l = [1, 2, 3] * 10000000
start = time.time()
y = [x[i] for i in l]
y = list(itertools.chain.from_iterable(y))
print(y[:6])
print(time.time() - start)
start = time.time()
y = []
[y.extend(x[i]) for i in l]
print(y[:6])
print(time.time() - start)
第一种方法是允许内部列表嵌套,然后在理解完成后将它们展平。第二种方法是在理解过程中构建一个平面列表。
似乎第一种方法更快一点:
3.8479559421539307
4.469805955886841
我想知道是否有更好(更快)的方法来做到这一点?
答案 0 :(得分:1)
答案实际上隐藏在你的问题中。实现目标的更快捷方式是:
l = [1, 2, 3]
x = {1:[1, 2], 2:[2, 3], 3:[3, 4]}
y = []
for k in l:
y.extend(x[k])
y *= 10000000
答案 1 :(得分:1)
我的机器上的时间更接近:
>>> x = {1: [1, 2], 2: [2, 3], 3: [3, 4]}
>>> l = [1, 2, 3] * 10000000
>>> import timeit
>>> import itertools
>>> def by_chaining():
... y = [x[i] for i in l]
... return list(itertools.chain.from_iterable(y))
...
>>> timeit.timeit(by_chaining, number=1)
5.491670315985385
>>> def by_extension():
... y = []
... [y.extend(x[i]) for i in l]
... return y
...
>>> timeit.timeit(by_extension, number=1)
5.656423713273028
事情是,.extend
方法实际上是用于显式循环,因为它是一个带副作用的函数。通过列表理解,您将生成一个单独的列表,其中包含None
值,然后将其丢弃,这会增加相当多的开销。参见:
>>> def by_extension_loop():
... y = []
... for i in l: y.extend(x[i])
... return y
...
>>> timeit.timeit(by_extension_loop, number=1)
4.62852763674681
让我们尝试一些其他方法:
>>> def by_nested_comprehension():
... return [e for i in l for e in x[i]]
...
>>> timeit.timeit(by_nested_comprehension, number=1)
5.102275806385393
不要这样做。它从理论上得到了正确的答案,但会永远占用过多的内存。 (这可能是sum
明确特殊地拒绝对字符串求和的原因的一部分 - 尽管我希望他们将其重定向到更有效的技术而不是)。
>>> def by_sum():
... return sum((x[i] for i in l), [])
如何让from_iterable
生成器与之合作?
>>> def by_chaining_generator():
... return list(itertools.chain.from_iterable(x[i] for i in l))
...
>>> timeit.timeit(by_chaining_generator, number=1)
5.420730297100135
如何使用map
代替理解?
>>> def by_chaining_map():
... return list(itertools.chain.from_iterable(map(x.get, l)))
...
>>> timeit.timeit(by_chaining_map, number=1)
4.707590696974194
>>> def by_chaining_map_2():
... return list(itertools.chain.from_iterable(map(x.__getitem__, l)))
...
>>> timeit.timeit(by_chaining_map_2, number=1)
4.576851915207953
看起来那个人可能是赢家,但它已经接近了。通过预分配和插入切片IIRC,还可以优化list.append
版本。但是我的创造力目前似乎已经筋疲力尽了:)