关于python:理解展平序列?

Comprehension for flattening a sequence of sequences?

本问题已经有最佳答案,请猛点这里访问。

如果我有序列序列(可能是元组列表),我可以使用itertools.chain()将其扁平化。但有时我觉得我宁愿把它写下来作为一种理解。我就是不知道怎么做。这是一个非常合理的案例:

假设我想交换序列中每对元素。我在这里使用一个字符串作为序列:

1
2
3
4
5
6
7
>>> 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来交换对。但最后我得到了一个需要展平的元组列表。所以我使用chain()。我能用理解来表达吗?

如果你想发表你自己的解决方案来解决交换成对元素的基本问题,继续,我会投票给任何能教给我新东西的人。但我只会将针对我的问题的答案标记为"接受",即使答案是"不,你不能"。


明白了吗?好。。。

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

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

1
2
3
4
5
6
7
8
9
10
11
12
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的列表中创建一个简单的列表

1
2
3
4
5
$ 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

我想到这一点是因为我有一种预感,即在幕后,extend将为列表分配适当的内存量,并且可能使用一些低级代码来移入项目。我不知道这是否是真的,但谁在乎呢,它更快。

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

1
2
3
4
5
$ 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),但这会比较慢,因为它正在构建自己的nones列表。

它还提供了不使用函数式编程的一些好处。即

  • 您可以扩展现有列表,而不是创建空列表,
  • 您仍然可以一眼、几分钟、几天甚至几个月后理解代码。

顺便说一句,最好避免列表理解。小的并不是很糟糕,但一般来说,列表理解并不能为您节省大量的打字时间,但通常更难理解,也很难更改或重构(是否见过三级列表理解?).google编码指南建议,除了在简单的情况下。我的观点是,它们只在"丢弃"代码中有用,即作者不关心可读性的代码,或者已知不需要未来维护的代码。

比较这两种写同样东西的方法:

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

用这个:

1
2
3
4
result = []
for sublist in l:
    for item in sublist:
        result.append(item)

嗯,但是第一个阻止了我,我不得不考虑。在第二种情况下,从凹口处可以明显地看到嵌套。


您可以使用Reduce来实现您的目标:

1
2
3
4
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)

这将返回一个元组而不是一个列表,因为原始列表中的元素是连接的元组。但是您可以很容易地从中构建一个列表,join方法也接受元组。

顺便说一句,列表理解不是正确的工具。基本上,列表理解通过描述这个列表的元素应该是什么样子来构建一个新的列表。您希望将元素列表减少到一个值。


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

或者,对于内部序列的类型不可知(只要它们都是相同的):

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