Cómo hacer una lista plana a partir de una lista de listas

4227

¿Existe un atajo para hacer una lista simple a partir de una lista de listas en Python?

Puedo hacerlo en un forbucle, pero ¿hay algo interesante?

Lo probé con functools.reduce():

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

Pero me sale este error:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
AttributeError: 'NoneType' object has no attribute 'extend'
3
  • 33
    Hay una discusión en profundidad de esto aquí: rightfootin.blogspot.com/2006/09/more-on-python-flatten.html , discutiendo varios métodos para aplanar listas de listas anidadas arbitrariamente. ¡Una lectura interesante! 4 de junio de 2009 a las 20:41
  • 9
    Algunas otras respuestas son mejores, pero la razón por la que la suya falla es que el método 'extender' siempre devuelve Ninguno. Para una lista con longitud 2, funcionará pero devolverá Ninguno. Para una lista más larga, consumirá los primeros 2 argumentos, lo que devuelve Ninguno. Luego continúa con None.extend (<tercer argumento>), lo que causa este error 11/06/2013 a las 21:48
  • stackoverflow.com/questions/50259290/… (este artículo explica la diferencia entre np.flatten () y tf.flatten () use (static vs dynamic) ndarray. 18 dic.20 a las 16:48
6001

Dada una lista de listas t,

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

lo que significa:

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

es más rápido que los atajos publicados hasta ahora. ( tes la lista para aplanar).

Aquí está la función correspondiente:

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

Como prueba, puede utilizar el timeitmódulo en la biblioteca estándar:

$ 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

Explicación: los accesos directos basados ​​en +(incluido el uso implícito en sum) son, por necesidad, O(T**2)cuando hay T sublistas, ya que la lista de resultados intermedios sigue alargándose, en cada paso se asigna un nuevo objeto de lista de resultados intermedios y todos los elementos en el resultado intermedio anterior debe copiarse (así como algunos nuevos agregados al final). Entonces, por simplicidad y sin pérdida real de generalidad, digamos que tiene T sublistas de k elementos cada una: los primeros k elementos se copian una y otra vez T-1 veces, los segundos k elementos T-2 veces, y así sucesivamente; número total de copias es K veces la suma de x para x de 1 a T excluidos, es decir, k * (T**2)/2.

La comprensión de la lista solo genera una lista, una vez, y copia cada elemento (desde su lugar de residencia original a la lista de resultados) también exactamente una vez.

5
  • 560
    Probé una prueba con los mismos datos, utilizando 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))'. Se ejecuta un poco más del doble de rápido que la comprensión de la lista anidada que es la más rápida de las alternativas que se muestran aquí. 15/10/10 a las 1:21
  • 318
    Encontré la sintaxis difícil de entender hasta que me di cuenta de que puede pensar en ella exactamente como bucles for anidados. para sublista en l: para artículo en sublista: artículo de rendimiento 27/07/11 a las 16:43
  • 201
    [hoja por árbol en bosque por hoja en árbol] podría ser más fácil de comprender y aplicar. 29 de agosto de 2013 a las 1:38
  • 1
    @RobCrowell Lo mismo aquí. Para mí, la comprensión de la lista que uno no lee bien , algo se siente mal al respecto; siempre parece que me equivoco y termino buscando en Google. Para mí esto se lee bien [leaf for leaf in tree for tree in forest]. Ojalá fuera así. Estoy seguro de que me estoy perdiendo algo sobre la gramática aquí, y agradecería que alguien pudiera señalarlo. 12 de julio a las 17:19
  • 4
    Seguí mirando aquí cada vez que quería aplanar una lista, pero este gif es lo que me llevó a casa: i.stack.imgur.com/0GoV5.gif 11 de agosto a las 12:04
1897

Puede utilizar itertools.chain():

import itertools

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

O puede usar itertools.chain.from_iterable()lo que no requiere descomprimir la lista con el *operador :

merged = list(itertools.chain.from_iterable(list2d))
2
  • 17
    El *es lo más complicado que hace chainmenos evidente que la lista por comprensión. Debe saber que la cadena solo une los iterables pasados ​​como parámetros, y el * hace que la lista de nivel superior se expanda en parámetros, por lo que chainune todos esos iterables, pero no desciende más. Creo que esto hace que la comprensión sea más legible que el uso de la cadena en este caso. 3 de septiembre de 2014 a las 14:13
  • 92
    @TimDierks: No estoy seguro de que "esto requiere que entiendas la sintaxis de Python" sea un argumento en contra del uso de una técnica determinada en Python. Seguro, el uso complejo puede confundir, pero el operador "splat" es generalmente útil en muchas circunstancias, y esto no lo usa de una manera particularmente oscura; Rechazar todas las características del lenguaje que no son necesariamente obvias para los usuarios principiantes significa que está atando una mano a la espalda. También puede descartar listas por comprensión mientras lo hace; los usuarios de otros orígenes encontrarán un forbucle que repetidamente appendes más obvio. 12/11/15 a las 20:26
1092

Nota del autor : esto es ineficaz. Pero divertido, porque los monoides son increíbles. No es apropiado para el código Python de producción.

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

Esto solo suma los elementos de iterable pasados ​​en el primer argumento, tratando el segundo argumento como el valor inicial de la suma (si no se da, 0se usa en su lugar y este caso le dará un error).

Debido a que está sumando listas anidadas, en realidad obtiene [1,3]+[2,4]como resultado de sum([[1,3],[2,4]],[]), que es igual a [1,3,2,4].

Tenga en cuenta que solo funciona en listas de listas. Para listas de listas de listas, necesitará otra solución.

5
  • 119
    eso es bastante ordenado e inteligente, pero no lo usaría porque es confuso de leer. 15/06/10 a las 18:55
  • 95
    Este es un Shlemiel el algoritmo del pintor joelonsoftware.com/articles/fog0000000319.html : innecesariamente ineficiente y innecesariamente feo. 25/04/12 a las 18:24
  • 49
    La operación de adición en listas forma a Monoid, que es una de las abstracciones más convenientes para pensar en una +operación en un sentido general (no limitado a números solamente). Entonces, esta respuesta merece un +1 de mi parte para el tratamiento (correcto) de las listas como un monoide. Sin embargo, la actuación es preocupante ...
    ulidtko
    3 de diciembre de 2014 a las 10:35
  • 13
    esta es una forma muy ineficiente debido al aspecto cuadrático de la suma. 31/07/2017 a las 18:04
  • 4
    Este artículo explica las matemáticas de la ineficiencia mathieularose.com/how-not-to-flatten-a-list-of-lists-in-python
    ds4940
    4 de enero de 2018 a las 16:46
669

Probé la mayoría de las soluciones sugeridas con perfplot (un proyecto favorito mío, esencialmente un envoltorio timeit) y encontré

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

para ser la solución más rápida, tanto cuando se concatenan muchas listas pequeñas como pocas listas largas. ( operator.iaddes igualmente rápido).

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí


Código para reproducir la trama:

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 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 numpy_concatenate(a):
    return list(numpy.concatenate(a))


perfplot.show(
    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,
    ],
    n_range=[2 ** k for k in range(16)],
    xlabel="num lists (of length 10)",
    # xlabel="len lists (10 lists total)"
)
4
  • 37
    Para listas anidadas enormes, 'list (numpy.array (a) .flat)' es la más rápida entre todas las funciones anteriores.
    Sara
    20/01/19 a las 13:57
  • ¿Hay alguna forma de hacer un perfplot en 3-d? número de matrices por tamaño medio de matriz?
    Leo
    30 abr.20 a las 0:31
  • @Sara, ¿puedes definir "enorme", por favor?
    Boris
    14 nov.20 a las 6:05
  • Probé numpy_flatel ejemplo de prueba de Rossetta Code ( enlace ) y obtuveVisibleDeprecationWarning: 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 5 dic.20 a las 11:08
232
>>> 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]

El extend()método en su ejemplo modifica en xlugar de devolver un valor útil (que functools.reduce()espera).

Una forma más rápida de hacer la reduceversión sería

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

Aquí hay un enfoque general que se aplica a números , cadenas , listas anidadas y contenedores mixtos . Esto puede aplanar contenedores simples y complicados (ver también Demo ).

Código

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

Notas :

  • En Python 3, yield from flatten(x)puede reemplazarfor sub_x in flatten(x): yield sub_x
  • En Python 3.8, las clases base abstractas se mueven desde collection.abcel typingmódulo.

Manifestación

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

Referencia

  • Esta solución se modifica de una receta en Beazley, D. y B. Jones. Receta 4.14, Python Cookbook 3rd Ed., O'Reilly Media Inc. Sebastopol, CA: 2013.
  • Encontré una publicación de SO anterior , posiblemente la demostración original.
8
  • 6
    Escribí más o menos lo mismo, porque no vi su solución ... esto es lo que busqué "aplanar recursivamente listas múltiples completas" ... (+1) 25 de marzo de 2017 a las 15:32
  • 3
    @MartinThoma Muy apreciado. Para su información, si acoplar iterables anidados es una práctica común para usted, hay algunos paquetes de terceros que lo manejan bien. Esto puede evitar reinventar la rueda. He mencionado, more_itertoolsentre otros, los comentados en este post. Salud.
    pylang
    25 de marzo de 2017 a las 17:51
  • Quizás traversetambién podría ser un buen nombre para esta forma de árbol, mientras que lo mantendría menos universal para esta respuesta al ceñirme a listas anidadas.
    Wolf
    15 de junio de 2017 a las 10:22
  • Puede verificar en if hasattr(x, '__iter__')lugar de importar / verificar Iterabley eso también excluirá las cadenas. 30/04/18 a las 16:46
  • el código anterior no parece funcionar si una de las listas anidadas tiene una lista de cadenas. [1, 2, [3, 4], [4], [], 9, 9.5, 'ssssss', ['str', 'sss', 'ss'], [3, 4, 5]] salida: - [1, 2, 3, 4, 4, 9, 9.5, 'ssssss', 3, 4, 5]
    sunnyX
    12/06/19 a las 21:35
67

Si desea aplanar una estructura de datos en la que no sabe qué tan profundo está anidado, puede usar 1iteration_utilities.deepflatten

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

Es un generador, por lo que debe enviar el resultado a listo iterar explícitamente sobre él.


Para aplanar solo un nivel y si cada uno de los elementos es en sí mismo iterable, también puede usar, iteration_utilities.flattenque en sí mismo es solo una envoltura delgada 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]

Solo para agregar algunos tiempos (según la respuesta de Nico Schlömer que no incluía la función presentada en esta respuesta):

Ingrese la descripción de la imagen aquí

Es una gráfica logarítmica para adaptarse a la amplia gama de valores abarcados. Para el razonamiento cualitativo: cuanto más bajo, mejor.

Los resultados muestran que si el iterable contiene solo unos pocos iterables internos, entonces sumserá más rápido; sin embargo, para los iterables largos, solo el itertools.chain.from_iterable, iteration_utilities.deepflatteno la comprensión anidada, tienen un rendimiento razonable al itertools.chain.from_iterableser el más rápido (como ya lo notó 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 Descargo de responsabilidad: soy el autor de esa biblioteca

0
36

Considere instalar el more_itertoolspaquete.

> pip install more_itertools

Se envía con una implementación para flatten( fuente , de las recetas de 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]

Nota: como se menciona en los documentos , flattenrequiere una lista de listas. Vea a continuación cómo aplanar entradas más irregulares.


A partir de la versión 2.4, puede aplanar iterables anidados más complicados con more_itertools.collapse( fuente , contribuido por 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]
3
  • Si puede permitirse agregar un paquete a su proyecto, esta respuesta es la mejor 5 mar.20 a las 15:53
  • falla cuando todos los elementos no están en la lista. (por ejemplo, lst = [1, [2,3]]). por supuesto, el número entero no es iterable. 8 de septiembre de 2020 a las 8:32
  • Además, tenga en cuenta que la lista de cadenas se acoplará a una lista de caracteres. 30 oct.20 a las 2:05
29

La razón por la que su función no funcionó es porque la extensión extiende una matriz en el lugar y no la devuelve. Todavía puede devolver x de lambda, usando algo como esto:

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

Nota: extender es más eficiente que + en listas.

2
  • 8
    extendse utiliza mejor como newlist = [], extend = newlist.extend, for sublist in l: extend(l)ya que evita la (bastante grande) por encima de la lambda, la búsqueda atributo en x, y la or.
    agf
    24/09/11 a las 10:12
  • para python 3 agregar from functools import reduce 2/07/19 a las 12:24
19

Lo siguiente me parece más simple:

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

matplotlib.cbook.flatten() funcionará para listas anidadas incluso si se anidan más profundamente que el ejemplo.

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

Resultado:

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

Esto es 18 veces más rápido que el subrayado ._. Aplanar:

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
13

También se puede usar el piso de NumPy :

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

Solo funciona cuando las sublistas tienen dimensiones idénticas.

0
8

puedes usar el list extendmétodo, muestra ser el más rápido:

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

rendimiento:

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

producción: ingrese la descripción de la imagen aquí

0
6

Si está dispuesto a renunciar a una pequeña cantidad de velocidad para lograr una apariencia más limpia, puede usar numpy.concatenate().tolist()o 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

Puede encontrar más información aquí en la documentación, numpy.concatenate y numpy.ravel .

3
  • 1
    No funciona para listas anidadas de manera desigual como [1, 2, [3], [[4]], [5, [6]]]
    EL_DON
    22/04/19 a las 21:39
  • @EL_DON por supuesto, eso no es lo que hace esta pregunta, hay otra pregunta que trata con ese caso 31 de julio a las 18:54
  • Sin embargo, @ juanpa.arrivillaga es una extensión simple y natural de la pregunta. Es más probable que las respuestas que pueden manejar una mayor profundidad de anidación sean útiles para alguien que encuentre esta pregunta.
    EL_DON
    2 de agosto a las 19:53
5
def flatten(alist):
    if alist == []:
        return []
    elif type(alist) is not list:
        return [alist]
    else:
        return flatten(alist[0]) + flatten(alist[1:])
1
  • Falla para python2.7 para la lista anidada de ejemplo en la pregunta: [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
    EL_DON
    22/04/19 a las 21:34
5

Nota : A continuación se aplica a Python 3.3+ porque usa yield_from. sixtambién es un paquete de terceros, aunque es estable. Alternativamente, podrías usar sys.version.


En el caso de obj = [[1, 2,], [3, 4], [5, 6]], todas las soluciones aquí son buenas, incluida la comprensión de listas y itertools.chain.from_iterable.

Sin embargo, considere este caso un poco más complejo:

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

Hay varios problemas aquí:

  • Un elemento, 6es solo un escalar; no es iterable, por lo que las rutas anteriores fallarán aquí.
  • Uno de los elementos, 'abc', es técnicamente iterable (todos strs son). Sin embargo, leyendo un poco entre líneas, no querrá tratarlo como tal, sino como un solo elemento.
  • El elemento final [8, [9, 10]]es en sí mismo un iterable anidado. Comprensión básica de listas y chain.from_iterablesólo extraer "1 nivel hacia abajo".

Puede remediar esto de la siguiente manera:

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

Aquí, verifica que el subelemento (1) sea iterable con Iterableun ABC de itertools, pero también desea asegurarse de que (2) el elemento no sea "similar a una cadena".

1
  • 1
    Si todavía está interesado en la compatibilidad con Python 2, cambie yield froma un forbucle, por ejemplofor x in flatten(i): yield x
    pylang
    19/06/18 a las 19:06
4

Puede que esta no sea la forma más eficiente, pero pensé en poner una sola línea (en realidad, una de dos líneas). Ambas versiones funcionarán en listas anidadas de jerarquías arbitrarias y aprovechan las características del lenguaje (Python3.5) y la recursividad.

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)

La salida es

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

Esto funciona primero en profundidad. La recursividad desciende hasta que encuentra un elemento que no está en la lista, luego extiende la variable local flisty luego la retrotrae al padre. Siempre que flistse devuelve, se amplía al padre flisten la comprensión de la lista. Por lo tanto, en la raíz, se devuelve una lista plana.

El anterior crea varias listas locales y las devuelve que se utilizan para ampliar la lista de los padres. Creo que la forma de evitar esto puede ser crear un gloabl flist, como se muestra a continuación.

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)

La salida es de nuevo

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

Aunque no estoy seguro en este momento sobre la eficiencia.

1
  • ¿Por qué extender ([l]) en lugar de agregar (l)?
    Maciek
    9 abr.20 a las 18:31
2

Otro enfoque inusual que funciona para listas heterogéneas y homogéneas de enteros:

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(',')]
7
  • Esa es solo una forma más complicada y un poco más lenta de lo que ᴡʜᴀᴄᴋᴀᴍᴀᴅᴏᴏᴅʟᴇ3000 ya publicó antes. Ayer reinventé su propuesta, por lo que este enfoque parece bastante popular en estos días;) 10 de enero de 2018 a las 22:03
  • No del todo: 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]
    tharndt
    11 de enero de 2018 a las 8:17
  • mi código como un trazador de líneas sería: flat_list = [int(e.replace('[','').replace(']','')) for e in str(deep_list).split(',')]
    tharndt
    11 de enero de 2018 a las 8:32
  • 1
    De hecho, tiene razón +1, la propuesta de ᴡʜᴀᴄᴋᴀᴍᴀᴅᴏᴏᴅʟᴇ3000 no funcionará con números de varios dígitos, tampoco probé esto antes, aunque debería ser obvio. Podrías simplificar tu código y escribir [int(e.strip('[ ]')) for e in str(deep_list).split(',')]. Pero sugeriría seguir con la propuesta de Deleet para casos de uso reales. No contiene transformaciones de tipo hacky, es más rápido y más versátil porque, naturalmente, también maneja listas con tipos mixtos. 11/01/18 a las 16:31
  • 2
    Lamentablemente no. Pero vi este código recientemente aquí: Python Practice Book 6.1.2
    tharndt
    15 de enero de 2018 a las 8:18
2

Hay varias respuestas con el mismo esquema de adición recursiva que se muestra a continuación, pero ninguna hace uso try, lo que hace que la solución sea más robusta y Pythonic .

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

Uso : este es un generador, por lo general desea encerrarlo en un constructor iterable como list()o tuple()o usarlo en un forbucle.

Las ventajas de esta solución son:

  • funciona con cualquier tipo de iterable (¡incluso con los futuros!)
  • funciona con cualquier combinación y profundidad de anidación
  • también funciona si el nivel superior contiene elementos básicos
  • sin dependencias
  • eficiente (puede aplanar el iterable anidado parcialmente, sin perder tiempo en la parte restante que no necesita)
  • versátil (puede usarlo para construir un iterable de su elección o en un bucle, ver más abajo)
3
  • ¿Por qué usarías una tupla? ahora tu solución es ineficaz. 31 de julio a las 18:53
  • Y con cualquier secuencia, sum((flatten(e) for e in itr), tuple())es muy ineficiente, 31 de julio a las 18:53
  • @ juanpa.arrivillaga Tu comentario me hizo pensar en mejorar mi respuesta y creo que encontré una mejor, ¿qué te parece?
    mmj
    1 de agosto a las 17:37
-2
np.hstack(listoflist).tolist()
1
  • Si bien este código puede responder a la pregunta, proporcionar un contexto adicional sobre por qué y / o cómo este código responde a la pregunta mejora su valor a largo plazo. Considere leer Cómo responder y editar su respuesta para mejorarla. 6 nov.20 a las 19:31