通过列表推导式将嵌套列表展平

16

我正在尝试使用Python中的列表推导式来展平一个列表。我的列表有点像这样

[[1, 2, 3], [4, 5, 6], 7, 8]

为了打印这个列表中的每个项目,我写了这个函数:

def flat(listoflist):
    for item in listoflist:
        if type(item) != list:
            print(item)
        else:
            for num in item:
                print(num)

交互式输出:

>>> flat(list1)
1
2
3
4
5
6
7
8

然后我使用相同的逻辑通过列表推导式来压平我的列表,但是我遇到了以下错误

list2 = [item if type(item) != list else num for num in item for item in list1]

这给了我以下错误:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable

我该如何使用列表推导式来展开这种嵌套列表?


这个回答解决了你的问题吗?如何将一个列表中的多个子列表展开成一个平面列表? - questionto42
在伪代码中,flatten(xs) = [ y | for x from xs , for y from ( if (type x == list) then flatten(x) else [x] ) ]。现在只需将其转换为有效的Python语法。 - Will Ness
列表将嵌套到什么级别? - Kayvan Shah
无论如何,发布了一个适用于嵌套列表的解决方案。 - Kayvan Shah
6个回答

28

没有人给出通常的答案:

def flat(l):
  return [y for x in l for y in x]

这个问题的重复版本在 StackOverflow 上一直存在。


6
你的代码不能处理OP提供的列表,因为他的列表包含非可迭代对象,即整数。但是,如果列表中的每个项目都是可迭代对象,则你的代码可以正常工作。请参见我的下面的评论。 - JDG
1
谢谢指出这点 - 实际上我需要找时间来摆弄一下并掌握它... - GreenAsJade
1
双重 for 循环很有趣。上述问题也可以使用以下方式解决:[z for y in [x if isinstance(x, list) else [x] for x in [1, [2]]] for z in y] - Nishant

13
>>> from collections import Iterable
>>> from itertools import chain

一句话描述:

>>> list(chain.from_iterable(item if isinstance(item,Iterable) and
                    not isinstance(item, basestring) else [item] for item in lis))
[1, 2, 3, 4, 5, 6, 7, 8]

可读性更好的版本:

>>> def func(x):                                         #use `str` in py3.x 
...     if isinstance(x, Iterable) and not isinstance(x, basestring): 
...         return x
...     return [x]
... 
>>> list(chain.from_iterable(func(x) for x in lis))
[1, 2, 3, 4, 5, 6, 7, 8]
#works for strings as well
>>> lis = [[1, 2, 3], [4, 5, 6], 7, 8, "foobar"]
>>> list(chain.from_iterable(func(x) for x in lis))                                                                
[1, 2, 3, 4, 5, 6, 7, 8, 'foobar']

使用嵌套列表推导式(与 itertools.chain 相比会慢些):

>>> [ele for item in (func(x) for x in lis) for ele in item]
[1, 2, 3, 4, 5, 6, 7, 8, 'foobar']

2
这对于使用2.x的提问者来说非常好,但值得注意的是,3.x用户应该使用str而不是basestring - Gareth Latty
@Ashwini 如果我的元素是字典类型而不是整数,应该怎么办? - Anurag Sharma
@AnuragSharma 有一个小例子吗? - Ashwini Chaudhary
[[{'ascdd': 'skj'}, {'fd': 'srsr'}], {'dsf': 'ds', 'ds': 'dsffd'}] 举例来说, @AshwiniChaudhary - Anurag Sharma
@AnuragSharma 这种情况的预期输出是什么? - Ashwini Chaudhary
@AshwiniChaudhary 预期输出 ---> [{'ascdd': 'skj'}, {'fd': 'srsr'},{'dsf': 'ds', 'ds': 'dsffd'}] 或者仅仅是一个字典列表。 - Anurag Sharma

3

您正在尝试迭代一个数字,这是不可能的(因此出现了错误)。

如果使用的是Python 2.7:

>>> from compiler.ast import flatten
>>> flatten(l)
[1, 2, 3, 4, 5, 6, 7, 8]

但请注意,该模块现已过时,在Python 3中不再存在。


1
类型检查列表是一个不好的想法。它使得代码更加缺乏灵活性。 - Gareth Latty
1
如果 l[[1, 2, 3], [4, 5, 6], 7, 888],第一个解决方案将产生 [1, 2, 3, 4, 5, 6, 7, 8, 8, 8] - falsetru
删除了前面的一部分,但是很遗憾我的另一个解决方案在Python 3中无法工作。 - TerryA

3

使用生成器的另一种解决方案:

import collections

def flatten(iterable):
    for item in iterable:
        if isinstance(item, collections.Iterable) and not isinstance(item, str):  # `basestring` < 3.x
            yield from item  # `for subitem in item: yield item` < 3.3
        else:
            yield item

>>> list(flatten([[1, 2, 3], [4, 5, 6], 7, 8]))
[1, 2, 3, 4, 5, 6, 7, 8]

2
def nnl(nl):    # non nested list

    nn = []

    for x in nl:
        if type(x) == type(5):
            nn.append(x)

    if type(x) == type([]):
        n = nnl(x)

        for y in n:
            nn.append(y)
    return nn

print (nnl([[9, 4, 5], [3, 8,[5]], 6]))  # output: [9, 4, 5, 3, 8, 5, 6]

可能一份解释也会有所帮助。 - scopchanov
这个解决方案使用递归。对于嵌套列表中的每个元素,您都会检查该元素是整数还是列表。如果是整数,则将该整数附加到列表nn中。如果嵌套列表中的元素是列表,则调用递归函数,您将对子列表执行与初始嵌套列表相同的操作。然后再次将不是列表的元素附加到nn中。 nn的输出将是一个非嵌套列表。祝大家编码愉快! - Alessandro Anderson
太好了!现在请编辑您的答案并添加这些信息。 - scopchanov
1
完成 - Alessandro Anderson

0
这是一个更加高效和快速的解决方案,使用精确度和生成器,并且可以处理高度嵌套的列表。
实现:
def _flatten_list(l: list):
    for item in l:
        if isinstance(item, list):
            yield from flatten_list(item)
        else:
            yield item
            
def flatten_list(l: list):
    return list(_flatten_list(l))
        
example = [1, 2, [3, 4, [5, 6], 7], 8, [9, [10]]]
print(flatten_list(example))

输出

> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接