我最近寻找一种方法来展平嵌套的python列表,如下所示:[[1,2,3],[4,5,6]],这样:[1,2,3,4,5 ,6]。
Stackoverflow一如既往地有用,我发现a post有这个巧妙的列表理解:
l = [[1,2,3],[4,5,6]]
flattened_l = [item for sublist in l for item in sublist]
我以为我理解了列表理解是如何工作的,但显然我还没有得到最微弱的想法。让我最困惑的是,除了上面的理解之外,这也是有效的(虽然它没有给出相同的结果):
exactly_the_same_as_l = [item for item in sublist for sublist in l]
有人可以解释python如何解释这些东西吗?基于第二个扩展,我希望python将它解释回来,但显然并非总是如此。如果是,第一次理解应该抛出错误,因为'子列表'不存在。我的思绪完全扭曲了,帮助!
答案 0 :(得分:6)
让我们来看看你的列表理解,但首先让我们从列表理解开始,这是最简单的。
l = [1,2,3,4,5]
print [x for x in l] # prints [1, 2, 3, 4, 5]
你可以像这样结构的for循环一样来看这个:
for x in l:
print x
现在让我们看看另一个:
l = [1,2,3,4,5]
a = [x for x in l if x % 2 == 0]
print a # prints [2,4]
这与此完全相同:
a = []
l = [1,2,3,4,5]
for x in l:
if x % 2 == 0:
a.append(x)
print a # prints [2,4]
现在让我们来看看你提供的例子。
l = [[1,2,3],[4,5,6]]
flattened_l = [item for sublist in l for item in sublist]
print flattened_l # prints [1,2,3,4,5,6]
对于列表理解,从最左边开始循环并继续工作。在这种情况下,变量,项目将被添加。它将产生这个等价物:
l = [[1,2,3],[4,5,6]]
flattened_l = []
for sublist in l:
for item in sublist:
flattened_l.append(item)
现在是最后一个
exactly_the_same_as_l = [item for item in sublist for sublist in l]
使用相同的知识,我们可以创建一个for循环并查看它的行为方式
for item in sublist:
for sublist in l:
exactly_the_same_as_l.append(item)
现在上述工作的唯一原因是因为在创建flattened_l时,它还创建了sublist
。这是一个范围界定的原因,为什么这不会引发错误。如果你在没有首先定义flattened_l的情况下运行它,那么你将获得NameError
答案 1 :(得分:2)
for循环从左到右进行评估。任何list comprehension
都可以重写为for循环,如下所示:
l = [[1,2,3],[4,5,6]]
flattened_l = []
for sublist in l:
for item in sublist:
flattened_l.append(item)
以上是用于展平列表的正确代码,无论您选择将其简洁地编写为列表解析,还是在此扩展版本中。
您编写的第二个列表解析会引发NameError,因为尚未定义“子列表”。您可以通过将列表推导写为for循环来看到这一点:
l = [[1,2,3],[4,5,6]]
flattened_l = []
for item in sublist:
for sublist in l:
flattened_l.append(item)
运行代码时没有看到错误的唯一原因是因为您在实现第一个列表推导时已经预先定义了子列表。
有关详细信息,您可能需要查看Guido's tutorial on list comprehensions。
答案 2 :(得分:1)
对于想要快速回答的懒惰开发者:
>>> a = [[1,2], [3,4]]
>>> [i for g in a for i in g]
[1, 2, 3, 4]
答案 3 :(得分:0)
当然,请注意,这种理解只会“变平”"列表列表(或其他迭代列表)。此外,如果你传递一个字符串列表,你就会变得扁平化#34;它成为一个字符列表。
为了以有意义的方式概括这一点,您首先希望能够清楚地区分字符串(或字节数组)和其他类型的序列(或其他Iterables)。所以让我们从一个简单的函数开始:
import collections
def non_str_seq(p):
'''p is putatively a sequence and not a string nor bytearray'''
return isinstance(p, collections.Iterable) and not (isinstance(p, str) or isinstance(p, bytearray))
使用它我们可以构建一个递归函数来展平任何
def flatten(s):
'''Recursively flatten any sequence of objects
'''
results = list()
if non_str_seq(s):
for each in s:
results.extend(flatten(each))
else:
results.append(s)
return results
可能有更优雅的方法来做到这一点。但这适用于我所知道的所有Python内置类型。简单对象(数字,字符串,None,True,False实例都返回包含在列表中。字典作为键列表返回(按哈希顺序)。
答案 4 :(得分:0)
尽管这种方法对于平整列表肯定 有效,但除非您的子列表非常小(每个元素只有1或2个元素),否则我不建议您这样做。
我对timeit
进行了一些分析,发现这比使用单个循环并调用extend所需的时间大约长2-3倍。
def flatten(l):
flattened = []
for sublist in l:
flattened.extend(sublist)
return flattened
虽然不那么漂亮,但速度却很重要。我认为这样做很好,因为extend
可以一次更有效地复制整个子列表,而不是一次复制一个元素。如果您知道子列表的大小为中到大,建议使用extend
。子列表越大,加速越大。
最后一个警告:,显然,只有当您需要急切地形成这个扁平化列表时,这才成立。例如,也许稍后您将对其进行排序。如果您最终只是按原样遍历列表,那么这将比使用其他人概述的嵌套循环方法更好。但是对于这种用例,您想返回一个生成器而不是一个列表,以增加懒惰的作用……
def flatten(l):
return (item for sublist in l for item in sublist) # note the parens