理解展平一系列序列? [复制]

Posted

技术标签:

【中文标题】理解展平一系列序列? [复制]【英文标题】:Comprehension for flattening a sequence of sequences? [duplicate] 【发布时间】:2010-10-02 05:10:04 【问题描述】:

如果我有序列序列(可能是元组列表),我可以使用 itertools.chain() 将其展平。但有时我觉得我宁愿把它写成一种理解。我只是不知道该怎么做。这是一个非常容易理解的案例:

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

>>> 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 来交换对。但我最终得到了一个现在需要展平的元组列表。所以我使用链()。有没有办法让我用理解来表达它?

如果您想针对交换对的元素的基本问题发布您自己的解决方案,请继续,我会投票赞成任何能教会我新知识的东西。但我只会将针对我的问题的答案标记为已接受,即使答案是“不,你不能。”。

【问题讨论】:

相关:Flattening a shallow list in Python 【参考方案1】:

您可以使用 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)

这会返回一个元组而不是一个列表,因为原始列表中的元素是连接在一起的元组。但是您可以轻松地从中构建一个列表,并且 join 方法也接受元组。

顺便说一下,列表推导式并不是正确的工具。基本上,列表推导式通过描述该列表的元素应该是什么样子来构建一个新列表。您希望将元素列表减少到只有一个值。

【讨论】:

-1:每次迭代创建一个新元组的开销在大列表上会太慢。此外,reduce(operator.add, X, Y) 是不可读的。请改用 sum(X, Y)。 对我来说 sum() 对算术运算的暗示太强了。 operator.add() 也是如此,但这就是我们必须用来表示 X + Y 的函数。所以我认为这比 sum() 更容易阅读。而且速度不在要求之内。 @heikogerlach:你真的认为 reduce(operator.add, X, Y) 比 sum(X, Y) 更具可读性???? reduce 几乎总是不可读的。出于这个原因,它已从 python3.0 中的 python 内置函数中删除。 for 循环几乎总是更具可读性。【参考方案2】:

理解?嗯……

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

【讨论】:

【参考方案3】:
>>> a = [(1, 2), (3, 4), (5, 6)]
>>> reduce(tuple.__add__, a)
>>> (1, 2, 3, 4, 5, 6)

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

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

【讨论】:

【参考方案4】:

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

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 尝试的示例快两倍多:Making a flat list out of list of lists in 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

我想出这个是因为我有一种预感,在幕后,extend 会为列表分配适量的内存,并且可能使用一些低级代码来移动项目。我不知道这是不是没错,但谁在乎呢,它更快。

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

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

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

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

顺便说一句,最好避免列表推导。小的并不算太糟糕,但总的来说,列表推导实际上并不能节省您的打字时间,但通常更难理解,也很难更改或重构(见过三级列表推导吗?)。 Google coding guidelines advise against them except in simple cases. 我的观点是它们只在“丢弃”代码中有用,即作者不关心可读性的代码,或者已知永远不需要将来维护的代码。

比较这两种写同一件事的方式:

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

用这个:

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

YMMV,但第一个让我停下了脚步,我不得不考虑一下。在第二个中,从缩进中可以看出嵌套。

【讨论】:

(对 Python 超级英雄 Alex 无冒犯)。 这是一个熟悉的问题,例如,列表理解和显式嵌套循环看起来与我相似(同样可读)。

以上是关于理解展平一系列序列? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

在 pandas 中展平系列,即元素为列表的系列

递归展平列表[重复]

如何“连接”或“组合”或“加入”一系列“二进制”序列化字节数组? [复制]

python 展平一系列Excel列,这些列在单元格中包含列表,同时保留行。允许为不可打印的U设置错误级别

Pandas系列将数组展平为csv [重复]

深入理解JAVA I/O系列五:对象序列化