如何在Python中将一个异构列表的列表展平为一个单一的列表?

4

我有一个对象列表,其中对象可以是列表或标量。我想要一个只包含标量的平坦列表。 例如:

L = [35,53,[525,6743],64,63,[743,754,757]]
outputList = [35,53,525,6743,64,63,743,754,757]

注意:此问题中的答案不适用于异构列表。

Python 中扁平化浅层列表

如果pop()一次仅返回一个标量,则此代码将起作用http://stackoverflow.com/a/10546929/1321404。您可以使用if len(returned_pop_element)>1修改此代码,然后为returned_pop_element(list)调用递归函数。 - Dmitry Zagorulkin
另请参阅:https://dev59.com/U2025IYBdhLWcg3wvIko#5828872 - sateesh
请参见 https://dev59.com/qnNA5IYBdhLWcg3wdtld#40857703。 - pylang
11个回答

10

这是一个相对简单的递归版本,它可以展平任何深度的列表。

l = [35,53,[525,6743],64,63,[743,754,757]]

def flatten(xs):
    result = []
    if isinstance(xs, (list, tuple)):
        for x in xs:
            result.extend(flatten(x))
    else:
        result.append(xs)
    return result

print flatten(l)

我认为我可以这样写 isinstance(xs,collections.Iterable) and not isinstance(xs,str),这样它就可以包括 set 和其他可能的可迭代对象了。 - balki
在 Python 3 之前,应该使用 and not isinstance(xs, basestring),但这是个好主意。 - Nick Craig-Wood

6

使用numpy可以整洁地一行完成。

import numpy as np
np.hstack(l)

你最终得到的是一个ndarray。
array([  35,   53,  525, 6743,   64,   63,  743,  754,  757])

5
>>> data = [35,53,[525,6743],64,63,[743,754,757]]
>>> def flatten(L):
        for item in L:
            if isinstance(item,list):
                for subitem in item:
                    yield subitem
            else:
                yield item


>>> list(flatten(data))
[35, 53, 525, 6743, 64, 63, 743, 754, 757]

这里是一行代码高尔夫的版本(它看起来不太好:D)

>>> [y for x in data for y in (x if isinstance(x,list) else [x])]
[35, 53, 525, 6743, 64, 63, 743, 754, 757]

1
第一个版本将字符串分解为字符,我认为这并不理想。 - Janne Karila
@JanneKarila,它并没有说会有字符串。 - jamylak
如果您使用hasattr(item, '__iter__'),则可以避免字符串问题,而不限制可迭代范围。 - Joel Cornett
@JoelCornett 这个问题只提到了使用 list,所以我会像其他答案一样使用 isinstance - jamylak

3
l = [35,53,[525,6743],64,63,[743,754,757]]
outputList = []

for i in l:
    if isinstance(i, list):
        outputList.extend(i)
    else:
        outputList.append(i)

@jamylak,感谢您的编辑,但我喜欢两个空格缩进 :-( - Vikas
@jamylak,感谢您提供的参考。我一直认为推荐使用空格,但不知道是4个空格。 - Vikas
当您按下“Tab”时,这些通常会被编辑器自动填充,通常默认设置为4或3。另外,由于某种原因,我无法在我的评论开头写@Vikas,我不知道为什么它不让我这样做... - jamylak

1

这是一个基于你提到的问题的一行代码:

list(itertools.chain(*((sl if isinstance(sl, list) else [sl]) for sl in l)))

更新:还有一个完全基于迭代器的版本:

from itertools import imap, chain
list(chain.from_iterable(imap(lambda x: x if isinstance(x, list) else [x], l)))

这是一行代码,它不需要很漂亮。 - ubik
1
那么在这种情况下,我认为我有一个更短的一行代码,我会发布它。 - jamylak
1
此外,sum((i if isinstance(i, list) else [i] for i in L), []) - Joel Cornett
@JoelCornett +1 我也是这样想的,但我不喜欢它需要在每次迭代中构建一个新列表。 - jamylak

1
outputList = []
for e in l:
    if type(e) == list:
        outputList += e
    else:
        outputList.append(e)

>>> outputList
[35, 53, 525, 6743, 64, 63, 743, 754, 757]

0
def nchain(iterable):
    for elem in iterable:
        if type(elem) is list:
            for elem2 in elem:
                yield elem2
        else:
            yield elem

0

递归函数,允许无限树深度:

def flatten(l):
    if isinstance(l,(list,tuple)):
        if len(l):
            return flatten(l[0]) + flatten(l[1:])
        return []
    else:
        return [l]

>>>flatten([35,53,[525,[1,2],6743],64,63,[743,754,757]])
[35, 53, 525, 1, 2, 6743, 64, 63, 743, 754, 757]

我试图避免使用isinstance以允许通用类型,但旧版本会在字符串上无限循环。现在它可以正确地展开字符串(不再是按字符展开,而是像假装字符串是标量一样)。


我不会期望字符串被压平(分解为单个字符)。 - Janne Karila
从技术上讲,字符串是可迭代的,这就是为什么我把它包含在内的原因。但当我更仔细地看它时,它似乎有点奇怪。 - Josiah

0
>>> L = [35,53,[525,6743],64,63,[743,754,757]]
>>> K = []
>>> [K.extend([i]) if type(i) == int else K.extend(i) for i in L ]
[None, None, None, None, None, None]
>>> K
[35, 53, 525, 6743, 64, 63, 743, 754, 757]

0

这个解决方案仅适用于您特定的情况(列表中的标量),并假设标量为整数。这是一个可怕的解决方案,但它非常简短。

outputlist = map(int,",".split(str(L).replace("[","").replace("]","")))

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