理解扁平序列的序列?

如果我有序列序列(也许是一个元组列表),我可以使用itertools.chain()来压扁它。 但有时候我觉得我宁愿把它写成理解。 我只是不知道该怎么做。 这是一个很有说服力的案例:

假设我想按顺序交换每一对的元素。 我在这里使用一个string作为序列:

>>> from itertools import chain >>> seq = '012345' >>> swapped_pairs = zip(seq[1::2], seq[::2]) >>> swapped_pairs [('1', '0'), ('3', '2'), ('5', '4')] >>> "".join(chain(*swapped_pairs)) '103254' 

我在序列的偶数和奇数片上使用zip来交换对。 但是,我最终得到了一个现在需要变平的元组列表。 所以我使用链()。 有没有一种方法可以用理解来expression呢?

如果你想把自己的解决scheme发布到交换对的元素这个基本问题上,那么我会投票给我一些新的教训。 但是,我只会将接受的答案标记为针对我的问题,即使答案是“不,你不能”。

有理解? 好…

 >>> seq = '012345' >>> swapped_pairs = zip(seq[1::2], seq[::2]) >>> ''.join(item for pair in swapped_pairs for item in pair) '103254' 

最快我发现是从一个空的数组开始,并扩展它:

 In [1]: a = [['abc', 'def'], ['ghi'],['xzy']] In [2]: result = [] In [3]: extend = result.extend In [4]: for l in a: ...: extend(l) ...: In [5]: result Out[5]: ['abc', 'def', 'ghi', 'xzy'] 

在Alex Martelli的尝试中,这个速度是以前的两倍: 从Python列表中取出一个列表

 $ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' '[item for sublist in l for item in sublist]' 10000 loops, best of 3: 86.3 usec per loop $ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'b = []' 'extend = b.extend' 'for sub in l:' ' extend(sub)' 10000 loops, best of 3: 36.6 usec per loop 

我想出了这个,因为我有一个背后的预感,扩展会为列表分配适量的内存,并可能使用一些低级代码来移动项目。我不知道这是真的,但谁在乎,这是更快。

顺便说一下,这只是一个线性加速:

 $ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]' 'b = []' 'extend = b.extend' 'for sub in l:' ' extend(sub)' 1000000 loops, best of 3: 0.844 usec per loop $ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]' '[item for sublist in l for item in sublist]' 1000000 loops, best of 3: 1.56 usec per loop 

你也可以使用map(results.extend, a) ,但是这样做比较慢,因为它正在构build自己的Nones列表。

它也给你一些不使用函数式编程的好处。 即

  • 你可以扩展现有的列表,而不是创build一个空的列表,
  • 您仍然可以在一分钟,几天甚至几个月的时间内了解代码。

顺便说一句,最好避免列表parsing。 小部分并不算太坏,但总的来说,列表理解实际上并不能为你节省很多打字的时间,但往往难以理解,而且很难改变或重构(从未见过三级列表理解?)。 除了简单的情况,Google编码指南build议不要这样做。 我的意见是,他们只是在'扔掉'的代码,即作者不关心可读性代码,或代码已知永远不需要将来的维护。

比较这两种写作方式:

 result = [item for sublist in l for item in sublist] 

有了这个:

 result = [] for sublist in l: for item in sublist: result.append(item) 

YMMV,但第一个阻止我的轨道,我不得不考虑。 在第二个嵌套是从缩进明显。

你可以使用reduce来实现你的目标:

 In [6]: import operator In [7]: a = [(1, 2), (2,3), (4,5)] In [8]: reduce(operator.add, a, ()) Out[8]: (1, 2, 2, 3, 4, 5) 

这将返回一个元组而不是一个列表,因为原始列表中的元素是连接的元组。 但是,你可以很容易地build立一个列表,并且连接方法也接受元组。

顺便说一句,列表理解不是正确的工具。 基本上,列表理解通过描述这个列表的元素应该是什么样子来build立一个新的列表。 您希望将元素列表仅缩小为一个值。

 >>> a = [(1, 2), (3, 4), (5, 6)] >>> reduce(tuple.__add__, a) >>> (1, 2, 3, 4, 5, 6) 

或者,对于内部序列的types是不可知的(只要它们都是一样的):

 >>> reduce(a[0].__class__.__add__, a)