在python中进行列表理解时展开列表

时间:2014-12-23 15:34:53

标签: python list python-3.x

以下是代码:

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

我想知道是否有更好(更快)的方法来做到这一点?

2 个答案:

答案 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版本。但是我的创造力目前似乎已经筋疲力尽了:)