【问题标题】:How to make a flat list out of a list of lists?如何从列表列表中制作平面列表?
【发布时间】:2022-01-09 17:54:21
【问题描述】:

在 Python 中是否有从列表列表中创建简单列表的快捷方式?

我可以在 for 循环中做到这一点,但是有一些很酷的“单线”吗?

我用functools.reduce()试了一下:

from functools import 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'

【问题讨论】:

  • 这里有一个深入的讨论:rightfootin.blogspot.com/2006/09/more-on-python-flatten.html,讨论了几种扁平化任意嵌套列表的方法。读起来很有趣!
  • 其他一些答案更好,但你失败的原因是'extend'方法总是返回None。对于长度为 2 的列表,它将工作但返回 None。对于更长的列表,它将消耗前 2 个 args,返回 None。然后它继续使用 None.extend(),这会导致这个错误
  • stackoverflow.com/questions/50259290/…(本文解释了 np.flatten() 和 tf.flatten() 使用(静态与动态)ndarray 之间的区别。
  • 这是一个很常见的重复目标。但是,对于 OP 已经有一个生成列表列表的过程(特别是如果它是一个列表推导式)的情况,请考虑 stackoverflow.com/questions/1077015/… 是否是更适用的副本。
  • 你的 lambda 应该是 reduce(lambda a, b: a + b, l)

标签: python list multidimensional-array flatten


【解决方案1】:

如果我想在之前的优秀答案中添加一些内容,这是我的递归 flatten 函数,它不仅可以展平嵌套列表,还可以展平任何给定的容器或任何通常可以抛出项目的任何对象。这也适用于任何深度的嵌套,它是一个惰性迭代器,可以根据要求生成项目:

def flatten(iterable):
    # These types won't considered a sequence or generally a container
    exclude = str, bytes

    for i in iterable:
        try:
            if isinstance(i, exclude):
                raise TypeError
            iter(i)
        except TypeError:
            yield i
        else:
            yield from flatten(i)

通过这种方式,您可以排除您不希望它们像str 或其他什么一样扁平化的类型。

这个想法是,如果一个对象可以通过iter(),它就可以生成项目了。因此,可迭代对象甚至可以将生成器表达式作为一个项目。

有人可能会争辩说,当 OP 没有要求时,你为什么写这个通用的?好的,你是对的。我只是觉得这可能会对某人有所帮助(就像对我自己一样)。

测试用例:

lst1 = [1, {3}, (1, 6), [[3, 8]], [[[5]]], 9, ((((2,),),),)]
lst2 = ['3', B'A', [[[(i ** 2 for i in range(3))]]], range(3)]

print(list(flatten(lst1)))
print(list(flatten(lst2)))

输出:

[1, 3, 1, 6, 3, 8, 5, 9, 2]
['3', b'A', 0, 1, 4, 0, 1, 2]

【讨论】:

    【解决方案2】:

    在列表理解中使用两个for

    l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
    flat_l = [e for v in l for e in v]
    print(flat_l)
    

    【讨论】:

      【解决方案3】:

      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
      

      【讨论】:

      • 我认为这是所有功能中最快的
      【解决方案4】:

      以下对我来说似乎最简单:

      >>> import numpy as np
      >>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
      >>> print(np.concatenate(l))
      [1 2 3 4 5 6 7 8 9]
      

      【讨论】:

      • OP 没有提到他们想使用 numpy。 Python 有很好的方法来做到这一点,而无需依赖库
      【解决方案5】:

      根据您的列表[[1, 2, 3], [4, 5, 6], [7], [8, 9]] 是1 级列表,我们可以简单地使用sum(list,[]) 而不使用任何库

      sum([[1, 2, 3], [4, 5, 6], [7], [8, 9]],[])
      # [1, 2, 3, 4, 5, 6, 7, 8, 9]
      

      【讨论】:

      【解决方案6】:

      一个非递归函数,用于展平任意深度的列表:

      def flatten_list(list1):
          out = []
          inside = list1
          while inside:
              x = inside.pop(0)
              if isinstance(x, list):
                  inside[0:0] = x
              else:
                  out.append(x)
          return out
      
      l = [[[1,2],3,[4,[[5,6],7],[8]]],[9,10,11]]
      flatten_list(l)
      # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
      

      【讨论】:

        【解决方案7】:

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

        import functools
        import operator
        functools.reduce(operator.iconcat, a, [])
        

        成为最快的解决方案,当连接许多小列表和很少的长列表时。 (operator.iadd 也同样快。)

        一个更简单且可接受的变体是

        out = []
        for sublist in a:
            out.extend(sublist)
        

        如果子列表的数量很大,这比上面的建议表现得差一点。


        重现情节的代码:

        import functools
        import itertools
        import operator
        
        import numpy as np
        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 functools_reduce_iconcat(a):
            return functools.reduce(operator.iconcat, a, [])
        
        
        def itertools_chain(a):
            return list(itertools.chain.from_iterable(a))
        
        
        def numpy_flat(a):
            return list(np.array(a).flat)
        
        
        def numpy_concatenate(a):
            return list(np.concatenate(a))
        
        
        def extend(a):
            out = []
            for sublist in a:
                out.extend(sublist)
            return out
        
        
        b = perfplot.bench(
            setup=lambda n: [list(range(10))] * n,
            # setup=lambda n: [list(range(n))] * 10,
            kernels=[
                forfor,
                sum_brackets,
                functools_reduce,
                functools_reduce_iconcat,
                itertools_chain,
                numpy_flat,
                numpy_concatenate,
                extend,
            ],
            n_range=[2 ** k for k in range(16)],
            xlabel="num lists (of length 10)",
            # xlabel="len lists (10 lists total)"
        )
        b.save("out.png")
        b.show()
        

        【讨论】:

        • 对于巨大的嵌套列表,'list(numpy.array(a).flat)'是上述所有函数中最快的。
        • 有没有办法做一个 3-d perfplot?数组的平均大小是多少?
        • @Sara 你能定义“巨大”吗?
        • 在 Rossetta Code (link) 的测试示例中尝试了 numpy_flat 并得到了 VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray
        • 上面遗漏了一个选项,对于我的特殊情况,它显示得更快,我只是 items = []; for sublist in a: items.extend(sublist); return sublist
        【解决方案8】:

        我想要一个可以处理多重嵌套的解决方案(例如[[1], [[[2]], [3]]], [1, 2, 3]),但也不是递归的(我有一个很大的递归级别,我遇到了一个递归错误。

        这是我想出的:

        def _flatten(l) -> Iterator[Any]:
            stack = l.copy()
            while stack:
                item = stack.pop()
                if isinstance(item, list):
                    stack.extend(item)
                else:
                    yield item
        
        
        def flatten(l) -> Iterator[Any]:
            return reversed(list(_flatten(l)))
        

        和测试:

        @pytest.mark.parametrize('input_list, expected_output', [
            ([1, 2, 3], [1, 2, 3]),
            ([[1], 2, 3], [1, 2, 3]),
            ([[1], [2], 3], [1, 2, 3]),
            ([[1], [2], [3]], [1, 2, 3]),
            ([[1], [[2]], [3]], [1, 2, 3]),
            ([[1], [[[2]], [3]]], [1, 2, 3]),
        ])
        def test_flatten(input_list, expected_output):
            assert list(flatten(input_list)) == expected_output
        

        【讨论】:

          【解决方案9】:

          您可以使用以下内容:

          def flatlst(lista):
              listaplana = []
              for k in lista: listaplana = listaplana + k
              return listaplana
          

          【讨论】:

          • + 运算符每次都会创建一个新列表。使用+=.extend() 会更好
          【解决方案10】:

          有几个答案与下面的递归附加方案相同,但没有一个使用try,这使得解决方案更加健壮和Pythonic

          def flatten(itr):
              for x in itr:
                  try:
                      yield from flatten(x)
                  except TypeError:
                      yield x
          

          用法:这是一个生成器,您通常希望将它包含在一个可迭代的构建器中,例如list()tuple(),或者在for 循环中使用它。

          此解决方案的优点是:

          • 适用于任何类型的可迭代(甚至是未来的!)
          • 适用于任何组合和嵌套深度
          • 如果顶层包含裸项目也可以使用
          • 无依赖关系
          • 高效(您可以将嵌套的可迭代部分展平,而不会在不需要的剩余部分上浪费时间)
          • 用途广泛(您可以使用它来构建您选择的迭代或循环)

          注意由于所有可迭代对象都被展平,因此字符串被分解为单个字符的序列。如果你不喜欢/不想要这样的行为,你可以使用以下版本来过滤掉像字符串和字节这样的扁平化迭代:

          def flatten(itr):
              if type(itr) in (str,bytes):
                  yield itr
              else:
                  for x in itr:
                      try:
                          yield from flatten(x)
                      except TypeError:
                          yield x
          

          【讨论】:

          • 为什么要使用元组?现在您的解决方案效率低下。
          • 而对于任何序列,sum((flatten(e) for e in itr), tuple()) 效率极低,
          • @juanpa.arrivillaga 你的评论让我考虑改进我的答案,我想我找到了一个更好的答案,你觉得呢?
          【解决方案11】:

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

          >>> 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]
          

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

          这是一个对数对数图,以适应跨越的巨大范围的值。对于定性推理:越低越好。

          结果表明,如果可迭代对象仅包含少数内部可迭代对象,则 sum 将是最快的,但是对于长可迭代对象,只有 itertools.chain.from_iterableiteration_utilities.deepflatten 或嵌套推导具有合理的性能,其中 itertools.chain.from_iterable 是最快(正如 Nico Schlö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 免责声明:我是该库的作者

          【讨论】:

            【解决方案12】:

            如果您愿意放弃一点点速度以获得更干净的外观,那么您可以使用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
            

            您可以在文档中找到更多信息,numpy.concatenatenumpy.ravel

            【讨论】:

            • 不适用于不均匀嵌套的列表,例如[1, 2, [3], [[4]], [5, [6]]]
            • @EL_DON 当然,这不是这个问题要问的,还有另一个问题涉及该案例
            • @juanpa.arrivillaga 这是一个简单而自然的问题延伸。可以处理更大嵌套深度的答案更有可能对发现此问题的人有用。
            【解决方案13】:

            也可以使用 NumPy 的flat

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

            仅当子列表具有相同的尺寸时才有效。

            【讨论】:

              【解决方案14】:

              给定列表列表t

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

              意思是:

              flat_list = []
              for sublist in t:
                  for item in sublist:
                      flat_list.append(item)
              

              比目前发布的快捷方式更快。 (t 是要展平的列表。)

              下面是对应的函数:

              def flatten(t):
                  return [item for sublist in t for item in sublist]
              

              作为证据,您可以使用标准库中的timeit 模块:

              $ python -mtimeit -s't=[[1,2,3],[4,5,6], [7], [8,9]]*99' '[item for sublist in t for item in sublist]'
              10000 loops, best of 3: 143 usec per loop
              $ python -mtimeit -s't=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(t, [])'
              1000 loops, best of 3: 969 usec per loop
              $ python -mtimeit -s't=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'reduce(lambda x,y: x+y,t)'
              1000 loops, best of 3: 1.1 msec per loop
              

              解释:基于+的快捷方式(包括sum中的隐含使用),当有T个子列表时,必然是O(T**2)——因为中间结果列表越来越长,每一步都有一个新的中间结果列表对象被分配,并且必须复制上一个中间结果中的所有项目(以及最后添加的一些新项目)。因此,为简单起见且不失一般性,假设您有 T 个子列表,每个子列表有 k 个项目:前 k 个项目被来回复制 T-1 次,后 k 个项目被复制 T-2 次,依此类推;总副本数是 x 的总和的 k 倍,因为 x 从 1 到 T 被排除,即k * (T**2)/2

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

              【讨论】:

              • 我尝试使用相同的数据进行测试,使用 itertools.chain.from_iterable : $ python -mtimeit -s'from itertools import chain; l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'list(chain.from_iterable(l))'。它的运行速度是此处显示的替代方案中最快的嵌套列表推导式的两倍多。
              • 我发现语法很难理解,直到我意识到你可以把它想象成嵌套的 for 循环。对于 l 中的子列表:对于子列表中的项目:yield 项目
              • [leaf for tree in forest for leaf in tree] 可能更容易理解和应用。
              • @RobCrowell 这里也一样。对我来说,列表理解不阅读正确,感觉有些不对劲——我似乎总是弄错并最终在谷歌上搜索。对我来说,这是正确的[leaf for leaf in tree for tree in forest]。我希望事情是这样的。我确信我在这里遗漏了一些关于语法的东西,如果有人能指出这一点,我将不胜感激。
              • 每次我想要展平列表时,我都会一直看这里,但这个 gif 是它的动力:i.stack.imgur.com/0GoV5.gif
              【解决方案15】:

              这是适用于数字字符串嵌套列表和混合容器的一般方法。这可以使简单和复杂的容器变平(另见 Demo)。

              代码

              from typing import Iterable 
              #from collections import Iterable                            # < py38
              
              
              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
              • 在 Python 3.8 中,abstract base classes 是从 collection.abctyping 模块的 moved

              演示

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

              参考

              • 此解决方案是根据 Beazley, D. 和 B. Jones 中的配方修改的。配方 4.14,Python Cookbook 第 3 版,O'Reilly Media Inc. Sebastopol,CA:2013。
              • 找到较早的SO post,可能是原始演示。

              【讨论】:

              • 我写的差不多,因为我没有看到你的解决方案......这就是我寻找的“递归展平完整的多个列表”......(+1)
              • @MartinThoma 非常感谢。仅供参考,如果扁平化嵌套迭代对您来说是一种常见的做法,那么有一些第三方包可以很好地处理这个问题。这可以避免重新发明轮子。我在这篇文章中提到过more_itertools。干杯。
              • 也许traverse 也可能是这种树的一个好名字,而我会通过坚持嵌套列表来减少这个答案的通用
              • 您可以检查if hasattr(x, '__iter__') 而不是导入/检查Iterable,这也将排除字符串。
              • 如果嵌套列表之一具有字符串列表,则上述代码似乎不起作用。 [1, 2, [3, 4], [4], [], 9, 9.5, 'ssssss', ['str', 'sss', 'ss'], [3, 4, 5]] 输出: - [1, 2, 3, 4, 4, 9, 9.5, 'ssssss', 3, 4, 5]
              【解决方案16】:

              作者注:这是低效的。但是很有趣,因为monoids 很棒。它不适用于生产 Python 代码。

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

              这只是将第一个参数中传递的 iterable 的元素相加,将第二个参数视为和的初始值(如果未给出,则使用 0 代替,这种情况下会报错)。

              因为您正在对嵌套列表求和,所以您实际上得到了[1,3]+[2,4] 作为sum([[1,3],[2,4]],[]) 的结果,它等于[1,3,2,4]

              请注意,仅适用于列表列表。对于列表列表,您将需要另一种解决方案。

              【讨论】:

              • 这非常简洁和聪明,但我不会使用它,因为它阅读起来很混乱。
              • 这是一个 Shlemiel 画家的算法 joelonsoftware.com/articles/fog0000000319.html -- 不必要的低效和不必要的丑陋。
              • 列表上的追加操作形成了一个Monoid,这是在一般意义上考虑+操作的最方便的抽象之一(不限于仅限数字)。因此,对于将列表(正确)视为幺半群,这个答案值得我 +1。 性能令人担忧……
              • 这是一种非常低效的方法,因为求和是二次方的。
              【解决方案17】:
              >>> from functools import reduce
              >>> l = [[1,2,3], [4,5,6], [7], [8,9]]
              >>> reduce(lambda x, y: x+y, l)
              [1, 2, 3, 4, 5, 6, 7, 8, 9]
              

              您示例中的extend() 方法修改了x,而不是返回一个有用的值(functools.reduce() 期望的)。

              reduce 版本的更快方法是

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

              【讨论】:

                【解决方案18】:

                你可以使用itertools.chain():

                import itertools
                
                list2d = [[1,2,3], [4,5,6], [7], [8,9]]
                merged = list(itertools.chain(*list2d))
                

                或者您可以使用itertools.chain.from_iterable(),这不需要使用* operator 解压缩列表:

                merged = list(itertools.chain.from_iterable(list2d))
                

                【讨论】:

                • * 是一个棘手的问题,它使chain 不如列表理解那么简单。您必须知道,chain 仅将作为参数传递的迭代连接在一起,而 * 导致顶级列表扩展为参数,因此 chain 将所有这些迭代连接在一起,但不会进一步下降。我认为这使得理解比在这种情况下使用链更具可读性。
                • @TimDierks:我不确定“这要求你理解 Python 语法”是否反对在 Python 中使用给定技术。当然,复杂的用法可能会让人感到困惑,但是“splat”运算符通常在许多情况下都很有用,而且这并不是以一种特别晦涩的方式使用它;拒绝所有对初学者来说不一定显而易见的语言功能意味着您将一只手绑在背后。当你在它的时候,也可以扔掉列表推导;来自其他背景的用户会发现for 循环重复appends 更明显。
                • * 创建一个中间元组。! from_iterable 直接从顶部列表中获取嵌套列表。
                • 为了使其更具可读性,您可以创建一个简单的函数:def flatten_list(deep_list: list[list[object]]):return list(chain.from_iterable(deep_list))。类型提示提高了正在发生的事情的清晰度(现代 IDE 会将其解释为返回 list[object] 类型)。
                【解决方案19】:
                np.hstack(listoflist).tolist()
                

                【讨论】:

                • 虽然此代码可能会回答问题,但提供有关此代码为何和/或如何回答问题的额外上下文可提高其长期价值。考虑阅读How to Answeredit 你的答案以改进它。
                【解决方案20】:

                考虑安装more_itertools 包。

                > pip install more_itertools
                

                它附带了flatten 的实现(source,来自itertools recipes):

                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]
                

                注意:正如docs 中提到的,flatten 需要一个列表列表。请参阅下面的扁平化更多不规则输入。


                从 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]
                

                【讨论】:

                • 如果你有能力为你的项目添加一个包 - 这个答案是最好的
                • 当所有元素都没有列出时它会失败。 (例如 lst=[1, [2,3]])。当然整数是不可迭代的。
                • 另外,请注意字符串列表将被展平为字符列表
                【解决方案21】:

                你可以使用listextend方法,它显示最快:

                flat_list = []
                for sublist in l:
                    flat_list.extend(sublist)
                

                性能:

                import functools
                import itertools
                import numpy
                import operator
                import perfplot
                
                
                
                def functools_reduce_iconcat(a):
                    return functools.reduce(operator.iconcat, a, [])
                
                
                def itertools_chain(a):
                    return list(itertools.chain.from_iterable(a))
                
                
                def numpy_flat(a):
                    return list(numpy.array(a).flat)
                
                
                def extend(a):
                    n = []
                
                    list(map(n.extend, a))
                
                    return n 
                
                
                perfplot.show(
                    setup=lambda n: [list(range(10))] * n,
                    kernels=[
                        functools_reduce_iconcat, extend,itertools_chain, numpy_flat
                        ],
                    n_range=[2**k for k in range(16)],
                    xlabel='num lists',
                    )
                

                输出:

                【讨论】:

                  【解决方案22】:

                  你的函数不起作用的原因是 extend 就地扩展了一个数组并且没有返回它。你仍然可以从 lambda 返回 x,使用如下代码:

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

                  注意:在列表中,extend 比 + 更有效。

                  【讨论】:

                  • extend 更适合用作newlist = []extend = newlist.extendfor sublist in l: extend(l),因为它避免了lambda 的(相当大的)开销、x 上的属性查找和or.
                  • 对于python 3添加from functools import reduce
                  【解决方案23】:

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

                  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(',')]
                  

                  【讨论】:

                  • 这只是 ᴡʜᴀᴄᴋᴀᴍᴀᴅᴏᴏᴅʟᴇ3000 之前发布的更复杂且更慢的方式。我昨天重新发明了他的提议,所以这种方法最近似乎很流行;)
                  • 不完全是:wierd_list = [[1, 2, 3], [4, 5, 6], [7], [8, 9], 10] >> nice_list=[1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 0]
                  • 我的代码作为一个班轮将是:flat_list = [int(e.replace('[','').replace(']','')) for e in str(deep_list).split(',')]
                  • 你确实是对的 +1,ᴡʜᴀᴄᴋᴀᴍᴀᴅᴏᴏᴅʟᴇ3000 的提议不适用于多位数字,我之前也没有测试过,尽管它应该很明显。您可以简化代码并编写[int(e.strip('[ ]')) for e in str(deep_list).split(',')]。但我建议坚持 Deleet 对实际用例的建议。它不包含 hacky 类型转换,它更快、更通用,因为它自然也可以处理混合类型的列表。
                  • 很遗憾没有。但我最近在这里看到了这段代码:Python Practice Book6.1.2
                  【解决方案24】:

                  注意:以下适用于 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 仅提取“向下一级”。

                  您可以通过以下方式解决此问题:

                  >>> 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) 是否可与 Iterable 迭代,这是来自 itertools 的 ABC,但还希望确保 (2) 元素 不是 "类似字符串。”

                  【讨论】:

                  • 如果您仍然对 Python 2 的兼容性感兴趣,请将 yield from 更改为 for 循环,例如for x in flatten(i): yield x
                  【解决方案25】:

                  这可能不是最有效的方法,但我想放一个单线(实际上是两线)。两个版本都适用于任意层次的嵌套列表,并利用语言特性 (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。因此,在根处,返回一个平面列表。

                  上面创建了几个本地列表并返回它们用于扩展父列表。我认为解决此问题的方法可能是创建一个 gloabl 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]
                  

                  虽然我目前不确定效率。

                  【讨论】:

                  • 为什么用extend([l])而不是append(l)?
                  【解决方案26】:
                  def flatten(alist):
                      if alist == []:
                          return []
                      elif type(alist) is not list:
                          return [alist]
                      else:
                          return flatten(alist[0]) + flatten(alist[1:])
                  

                  【讨论】:

                  • 对于问题中的示例嵌套列表,python2.7 失败:[[1, 2, 3], [4, 5, 6], [7], [8, 9]]
                  猜你喜欢
                  • 2021-11-28
                  相关资源
                  最近更新 更多