How does the list comprehension to flatten a python list work?
我最近寻找一种方法将嵌套的python列表扁平化,如:【1,2,3】、【4,5,6】;到这个列表中:【1,2,3,4,5,6】。
StackOverflow和以前一样有用,我找到了一篇具有这种巧妙列表理解能力的文章:
1 2 | l = [[1,2,3],[4,5,6]] flattened_l = [item for sublist in l for item in sublist] |
我以为我理解了列表理解是如何工作的,但显然我还没有一个最模糊的概念。最让我困惑的是,除了上述的理解之外,这也同样存在(尽管它没有给出相同的结果):
1 | exactly_the_same_as_l = [item for item in sublist for sublist in l] |
号
有人能解释一下python是如何解释这些东西的吗?基于第二个压缩,我希望Python能够将其解释回前面,但显然情况并非总是如此。如果是这样,第一次理解应该抛出一个错误,因为"子列表"不存在。我的思想完全扭曲了,救命!
让我们看看你的列表理解,但首先让我们从列表理解开始,它是最简单的。
1 2 | l = [1,2,3,4,5] print [x for x in l] # prints [1, 2, 3, 4, 5] |
您可以将其与这样的for循环结构相同:
1 2 | for x in l: print x |
。
现在让我们来看另一个:
1 2 3 | l = [1,2,3,4,5] a = [x for x in l if x % 2 == 0] print a # prints [2,4] |
与此完全相同:
1 2 3 4 5 6 | a = [] l = [1,2,3,4,5] for x in l: if x % 2 == 0: a.append(x) print a # prints [2,4] |
。
现在让我们来看一下您提供的示例。
1 2 3 | 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] |
。
对于列表理解,从最左边的for循环开始,并按您的方式进行。在这种情况下,变量item将被添加。它将产生这种等效物:
1 2 3 4 5 | l = [[1,2,3],[4,5,6]] flattened_l = [] for sublist in l: for item in sublist: flattened_l.append(item) |
现在是最后一个了
1 | exactly_the_same_as_l = [item for item in sublist for sublist in l] |
。
使用相同的知识,我们可以创建一个for循环,并查看它的行为
1 2 3 | for item in sublist: for sublist in l: exactly_the_same_as_l.append(item) |
现在唯一的原因是上面的一个作品是因为当扁平的"l"被创造出来的时候,它也创造了"EDOCX1"(1)。这是不引发错误的一个范围界定原因。如果你不先定义扁平的,你会得到一个
for循环从左到右进行计算。任何
1 2 3 4 5 | l = [[1,2,3],[4,5,6]] flattened_l = [] for sublist in l: for item in sublist: flattened_l.append(item) |
以上是压平列表的正确代码,无论您是选择以列表理解的形式简洁地编写列表,还是在此扩展版本中编写列表。
由于尚未定义"子列表",您编写的第二个列表理解将引发一个名称错误。您可以通过将列表理解写成for循环来看到这一点:
1 2 3 4 5 | l = [[1,2,3],[4,5,6]] flattened_l = [] for item in sublist: for sublist in l: flattened_l.append(item) |
号
运行代码时没有看到错误的唯一原因是,在实现第一个列表理解时,您以前定义了子列表。
有关更多信息,您可以查看guido关于列表理解的教程。
对于需要快速回答的懒惰开发人员:
1 2 3 | >>> a = [[1,2], [3,4]] >>> [i for g in a for i in g] [1, 2, 3, 4] |
号
虽然这种方法确实适用于扁平化列表,但我不建议这样做,除非知道子列表非常小(每个子列表有1或2个元素)。
我对
1 2 3 4 5 | def flatten(l): flattened = [] for sublist in l: flattened.extend(sublist) return flattened |
号
虽然不是很漂亮,但加速很重要。我认为这样做很好,因为
最后一个警告:很明显,这只有在您需要急切地形成这个扁平的列表时才成立。例如,您可能稍后会对其进行排序。如果您最终只按原样遍历列表,那么这不会比使用其他人概述的嵌套循环方法更好。但是对于这个用例,为了增加懒惰的好处,您希望返回一个生成器而不是一个列表…
1 2 | def flatten(l): return (item for sublist in l for item in sublist) # note the parens |
号
当然,请注意,这种理解只会"压扁"列表(或其他iterables的列表)。另外,如果你给它一个字符串列表,你会把它"展平"成一个字符列表。
要以有意义的方式概括这一点,首先您希望能够清楚地区分字符串(或bytearray)和其他类型的序列(或其他iterables)。那么让我们从一个简单的函数开始:
1 2 3 4 | 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)) |
。
使用它,我们可以构建一个递归函数来扁平任何
1 2 3 4 5 6 7 8 9 10 | 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的实例都将被包装在列表中返回。字典作为键列表返回(按哈希顺序)。