python多個list - python如何创建一个空的list



如何从列表列表中制作一个平面列表? (20)

我想知道是否有一条快捷方式可以在Python列表中列出一个简单的列表。

我可以在for循环中做到这一点,但也许有一些很酷的“单行”? 我用reduce尝试了,但是我收到了一个错误。

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
reduce(lambda x, y: x.extend(y), l)

错误信息

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
AttributeError: 'NoneType' object has no attribute 'extend'

https://ffff65535.com


underscore.py包风扇的简单代码

from underscore import _
_.flatten([[1, 2, 3], [4, 5, 6], [7], [8, 9]])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

它解决了所有展平问题(无列表项或复杂嵌套)

from underscore import _
# 1 is none list item
# [2, [3]] is complex nesting
_.flatten([1, [2, [3]], [4, 5, 6], [7], [8, 9]])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

您可以使用pip安装underscore.py

pip install underscore.py

matplotlib.cbook.flatten()将适用于嵌套列表,即使它们比示例嵌套更深。

import matplotlib
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
print(list(matplotlib.cbook.flatten(l)))
l2 = [[1, 2, 3], [4, 5, 6], [7], [8, [9, 10, [11, 12, [13]]]]]
print list(matplotlib.cbook.flatten(l2))

结果:

[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]

这比下划线快18倍._。flatten:

Average time over 1000 trials of matplotlib.cbook.flatten: 2.55e-05 sec
Average time over 1000 trials of underscore._.flatten: 4.63e-04 sec
(time for underscore._)/(time for matplotlib.cbook) = 18.1233394636

我接受我的陈述。 总和不是赢家。 虽然列表很小但速度更快。 但是,较大的列表会使性能显着下降。

>>> timeit.Timer(
        '[item for sublist in l for item in sublist]',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10000'
    ).timeit(100)
2.0440959930419922

总和版本仍然运行超过一分钟,它还没有完成处理!

对于中型名单:

>>> timeit.Timer(
        '[item for sublist in l for item in sublist]',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
    ).timeit()
20.126545906066895
>>> timeit.Timer(
        'reduce(lambda x,y: x+y,l)',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
    ).timeit()
22.242258071899414
>>> timeit.Timer(
        'sum(l, [])',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
    ).timeit()
16.449732065200806

使用小列表和timeit:number = 1000000

>>> timeit.Timer(
        '[item for sublist in l for item in sublist]',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
    ).timeit()
2.4598159790039062
>>> timeit.Timer(
        'reduce(lambda x,y: x+y,l)',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
    ).timeit()
1.5289170742034912
>>> timeit.Timer(
        'sum(l, [])',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
    ).timeit()
1.0598428249359131

注意:以下内容适用于Python 3.3+,因为它使用yield_fromsix也是第三方包,虽然它是稳定的。或者,您可以使用sys.version

在这种情况下obj = [[1, 2,], [3, 4], [5, 6]],这里的所有解决方案都很好,包括列表理解和itertools.chain.from_iterable

但是,请考虑这个稍微复杂的情况:

>>> obj = [[1, 2, 3], [4, 5], 6, 'abc', [7], [8, [9, 10]]]

这里有几个问题:

  • 一个元素,6只是一个标量; 它不可迭代,因此上述路线将在这里失败。
  • 其中一个要素,'abc'技术上可迭代(所有strs为)。但是,稍微阅读一下这些行,您不希望将其视为这样 - 您希望将其视为单个元素。
  • 最后一个元素[8, [9, 10]]本身就是一个嵌套的可迭代元素。基本列表理解,chain.from_iterable只提取“1级下来”。

您可以按如下方式解决此问题:

>>> from collections import Iterable
>>> from six import string_types

>>> def flatten(obj):
...     for i in obj:
...         if isinstance(i, Iterable) and not isinstance(i, string_types):
...             yield from flatten(i)
...         else:
...             yield i


>>> list(flatten(obj))
[1, 2, 3, 4, 5, 6, 'abc', 7, 8, 9, 10]

在这里,您检查子元素(1)是否可以与IterableABC 迭代itertools,但也要确保(2)元素不是 “类似字符串”。


为什么使用extend?

reduce(lambda x, y: x+y, l)

这应该工作正常。


也可以使用NumPy的flat

import numpy as np
list(np.array(l).flat)

编辑11/02/2016:仅当子列表具有相同的尺寸时才有效。


以下是适用于数字字符串嵌套列表和混合容器的一般方法。

from collections import Iterable


def flatten(items):
    """Yield items from any nested iterable; see Reference."""
    for x in items:
        if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
            for sub_x in flatten(x):
                yield sub_x
        else:
            yield x

注意:在Python 3中, yield from flatten(x)可以替换for sub_x in flatten(x): yield sub_x

演示

lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(flatten(lst))                                         # nested lists
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

mixed = [[1, [2]], (3, 4, {5, 6}, 7), 8, "9"]              # numbers, strs, nested & mixed
list(flatten(mixed))
# [1, 2, 3, 4, 5, 6, 7, 8, '9']

参考

  • 该解决方案是根据Beazley,D。和B. Jones的配方修改的 食谱4.14,Python Cookbook 3rd Ed。,O'Reilly Media Inc. Sebastopol,CA:2013。
  • 找到了早期的SO帖子 ,可能是最初的演示。

似乎与operator.add混淆了! 当您将两个列表一起添加时,正确的术语是concat ,而不是添加。 operator.concat是您需要使用的。

如果您正在考虑功能,它就像这样简单::

>>> list2d = ((1, 2, 3), (4, 5, 6), (7,), (8, 9))
>>> reduce(operator.concat, list2d)
(1, 2, 3, 4, 5, 6, 7, 8, 9)

你看到reduce尊重序列类型,所以当你提供一个元组时,你会得到一个元组。 让我们尝试一下清单::

>>> list2d = [[1, 2, 3],[4, 5, 6], [7], [8, 9]]
>>> reduce(operator.concat, list2d)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

啊哈,你得到一份清单。

性能怎么样::

>>> list2d = [[1, 2, 3],[4, 5, 6], [7], [8, 9]]
>>> %timeit list(itertools.chain.from_iterable(list2d))
1000000 loops, best of 3: 1.36 µs per loop

from_iterable非常快! 但是用concat减少它是没有可比性的。

>>> list2d = ((1, 2, 3),(4, 5, 6), (7,), (8, 9))
>>> %timeit reduce(operator.concat, list2d)
1000000 loops, best of 3: 492 ns per loop

你可以使用numpy:
flat_list = list(np.concatenate(list_of_list))


另一种适用于异构和同类整数列表的不寻常方法:

from typing import List


def flatten(l: list) -> List[int]:
    """Flatten an arbitrary deep nested list of lists of integers.

    Examples:
        >>> flatten([1, 2, [1, [10]]])
        [1, 2, 1, 10]

    Args:
        l: Union[l, Union[int, List[int]]

    Returns:
        Flatted list of integer
    """
    return [int(i.strip('[ ]')) for i in str(l).split(',')]

如果你想展平一个你不知道嵌套深度的数据结构,你可以使用iteration_utilities.deepflatten 1

>>> from iteration_utilities import deepflatten

>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> list(deepflatten(l, depth=1))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> l = [[1, 2, 3], [4, [5, 6]], 7, [8, 9]]
>>> list(deepflatten(l))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

它是一个生成器,因此您需要将结果转换为list或显式迭代它。

要展平只有一个级别,如果每个项本身都是可迭代的,你也可以使用iteration_utilities.flatten ,它本身只是itertools.chain.from_iterable一个瘦包装:

>>> from iteration_utilities import flatten
>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> list(flatten(l))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

只是添加一些时间(基于NicoSchlömer的回答,不包括此答案中提供的功能):

这是一个对数日志图,可以适应各种各样的值。 对于定性推理:越低越好。

结果表明,如果iterable只包含几个内部迭代,那么sum将是最快的,但是对于长迭代,只有itertools.chain.from_iterableiteration_utilities.deepflatten或嵌套的理解具有合理的性能, itertools.chain.from_iterable是最快的(正如NicoSchlömer已经注意到的那样)。

from itertools import chain
from functools import reduce
from collections import Iterable  # or from collections.abc import Iterable
import operator
from iteration_utilities import deepflatten

def nested_list_comprehension(lsts):
    return [item for sublist in lsts for item in sublist]

def itertools_chain_from_iterable(lsts):
    return list(chain.from_iterable(lsts))

def pythons_sum(lsts):
    return sum(lsts, [])

def reduce_add(lsts):
    return reduce(lambda x, y: x + y, lsts)

def pylangs_flatten(lsts):
    return list(flatten(lsts))

def flatten(items):
    """Yield items from any nested iterable; see REF."""
    for x in items:
        if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
            yield from flatten(x)
        else:
            yield x

def reduce_concat(lsts):
    return reduce(operator.concat, lsts)

def iteration_utilities_deepflatten(lsts):
    return list(deepflatten(lsts, depth=1))


from simple_benchmark import benchmark

b = benchmark(
    [nested_list_comprehension, itertools_chain_from_iterable, pythons_sum, reduce_add,
     pylangs_flatten, reduce_concat, iteration_utilities_deepflatten],
    arguments={2**i: [[0]*5]*(2**i) for i in range(1, 13)},
    argument_name='number of inner lists'
)

b.plot()

1免责声明:我是该图书馆的作者


如果你愿意放弃一numpy.concatenate().tolist()速度以获得更清晰的外观,那么你可以使用numpy.concatenate().tolist()numpy.concatenate().ravel().tolist()

import numpy

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]] * 99

%timeit numpy.concatenate(l).ravel().tolist()
1000 loops, best of 3: 313 µs per loop

%timeit numpy.concatenate(l).tolist()
1000 loops, best of 3: 312 µs per loop

%timeit [item for sublist in l for item in sublist]
1000 loops, best of 3: 31.5 µs per loop

您可以在docs numpy.concatenatenumpy.ravel找到更多numpy.ravel


您的函数不起作用的原因:extend将数组扩展到原位并且不返回它。 你仍然可以使用一些技巧从lambda返回x:

reduce(lambda x,y: x.extend(y) or x, l)

注意:extend比列表上的+更有效。


我找到的最快的解决方案(无论如何都是大型列表):

import numpy as np
#turn list into an array and flatten()
np.array(l).flatten()

完成! 您当然可以通过执行list(l)将其转回列表


我用perfplot测试了大多数建议的解决方案(我的一个宠物项目,基本上是timeit的包装),并且发现了

list(itertools.chain.from_iterable(a))

成为最快的解决方案(如果连接的列表超过10个)。

重现情节的代码:

import functools
import itertools
import numpy
import operator
import perfplot


def forfor(a):
    return [item for sublist in a for item in sublist]


def sum_brackets(a):
    return sum(a, [])


def functools_reduce(a):
    return functools.reduce(operator.concat, a)


def itertools_chain(a):
    return list(itertools.chain.from_iterable(a))


def numpy_flat(a):
    return list(numpy.array(a).flat)


def numpy_concatenate(a):
    return list(numpy.concatenate(a))


perfplot.show(
    setup=lambda n: [list(range(10))] * n,
    kernels=[
        forfor, sum_brackets, functools_reduce, itertools_chain, numpy_flat,
        numpy_concatenate
        ],
    n_range=[2**k for k in range(16)],
    logx=True,
    logy=True,
    xlabel='num lists'
    )

考虑安装more_itertools包。

> pip install more_itertools

它附带了flattensource ,来自itertools配方 )的实现:

import more_itertools


lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(more_itertools.flatten(lst))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

从版本2.4开始,您可以使用more_itertools.collapsesource ,由abarnet提供)来展平更复杂的嵌套迭代。

lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(more_itertools.collapse(lst)) 
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

lst = [[1, 2, 3], [[4, 5, 6]], [[[7]]], 8, 9]              # complex nesting
list(more_itertools.collapse(lst))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

这可能不是最有效的方式,但我想放一个单线(实际上是一个双线)。两个版本都可以在任意层次结构嵌套列表上运行,并利用语言功能(Python3.5)和递归。

def make_list_flat (l):
    flist = []
    flist.extend ([l]) if (type (l) is not list) else [flist.extend (make_list_flat (e)) for e in l]
    return flist

a = [[1, 2], [[[[3, 4, 5], 6]]], 7, [8, [9, [10, 11], 12, [13, 14, [15, [[16, 17], 18]]]]]]
flist = make_list_flat(a)
print (flist)

输出是

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

这首先是深度工作。递归向下,直到找到非列表元素,然后扩展局部变量flist,然后将其回滚到父级。每当flist返回时,它都会扩展到flist列表推导中的父级。因此,在根目录下,返回一个平面列表。

上面的一个创建了几个本地列表并返回它们,用于扩展父列表。我认为解决这个问题的方法可能就是创造一个flist像我这样的人。

a = [[1, 2], [[[[3, 4, 5], 6]]], 7, [8, [9, [10, 11], 12, [13, 14, [15, [[16, 17], 18]]]]]]
flist = []
def make_list_flat (l):
    flist.extend ([l]) if (type (l) is not list) else [make_list_flat (e) for e in l]

make_list_flat(a)
print (flist)

输出再次出现

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

虽然我现在还不确定效率。


def flatten(alist):
    if alist == []:
        return []
    elif type(alist) is not list:
        return [alist]
    else:
        return flatten(alist[0]) + flatten(alist[1:])

flat_list = []
for i in list_of_list:
    flat_list+=i

此代码也可以正常工作,因为它只是一直扩展列表。 虽然它非常相似,但只有一个用于循环。 因此它比为循环添加2更少复杂。


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

意思是:

for sublist in l:
    for item in sublist:
        flat_list.append(item)

比目前发布的快捷方式快。 ( l是要压扁的列表。)

这是一个相应的功能:

flatten = lambda l: [item for sublist in l for item in sublist]

为了证据,您可以像往常一样使用标准库中的timeit模块:

$ 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: 143 usec per loop
$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])'
1000 loops, best of 3: 969 usec per loop
$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'reduce(lambda x,y: x+y,l)'
1000 loops, best of 3: 1.1 msec per loop

说明:当存在L个子列表时,基于+的快捷方式(包括sum隐含的用法)必然为O(L**2) - 因为中间结果列表持续变长,在每个步骤都有新的中间结果列表对象被分配,并且必须复制先前中间结果中的所有项目(以及最后添加的一些新项目)。 所以(为了简单而没有实际的失去一般性)说你有每个项目的L个子列表:第一个I项目来回复制L-1次,第二个I项目L-2次,依此类推; 总复制数是I乘以x的总和,从1到L排除,即I * (L**2)/2

列表理解只生成一个列表一次,并将每个项目(从其原始居住地点到结果列表)复制一次。





flatten