Pregunta

Me pregunto si hay un atajo para hacer una lista simple a partir de una lista de listas en Python.

Puedo hacer eso en un for bucle, pero ¿quizás haya algún "frase ingeniosa" interesante?lo probé con reducir, pero me sale un error.

Código

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

Mensaje de 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'
¿Fue útil?

Solución

Dada una lista de listas l,

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

lo que significa:

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

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

Aquí está la función correspondiente:

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

Como prueba se puede utilizar el timeit módulo en la biblioteca estándar:

$ 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

Explicación:los atajos basados ​​en + (incluido el uso implícito en sum) son, necesariamente, O(L**2) cuando hay L sublistas, a medida que la lista de resultados intermedios se hace más larga, en cada paso se asigna un nuevo objeto de lista de resultados intermedios y se deben copiar todos los elementos del resultado intermedio anterior (así como algunos nuevos agregados). al final).Entonces, por simplicidad y sin pérdida real de generalidad, digamos que tiene L sublistas de I elementos cada una:los primeros elementos I se copian de un lado a otro L-1 veces, los segundos I elementos L-2 veces, y así sucesivamente;El número total de copias es I multiplicado por la suma de x para x de 1 a L excluido, es decir, I * (L**2)/2.

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

Otros consejos

Puede utilizar itertools.chain() :

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

o, en Python> = 2,6, utilice itertools.chain.from_iterable() que no requiere de desembalar la lista:

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

Este enfoque es sin duda más fácil de leer que [item for sublist in l for item in sublist] y parece ser más rápido también:

[me@home]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99;import itertools' 'list(itertools.chain.from_iterable(l))'
10000 loops, best of 3: 24.2 usec per loop
[me@home]$ 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: 45.2 usec per loop
[me@home]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])'
1000 loops, best of 3: 488 usec per loop
[me@home]$ 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: 522 usec per loop
[me@home]$ python --version
Python 2.7.3

Nota del autor : Esto es ineficiente. Pero divertido, porque monoides son impresionantes. No es apropiado para la producción de código Python.

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

Esto se suma los elementos de iterable pasado en el primer argumento, el tratamiento del segundo argumento como el valor inicial de la suma (si no se da, se utiliza en lugar 0 este caso y le dará un error).

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

Tenga en cuenta que sólo funciona en listas de listas. Para las listas de listas de listas, tendrá otra solución.

I probado soluciones más sugeridas con perfplot (un proyecto animal de la mía, esencialmente una envoltura alrededor de timeit) y encontrados

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

a ser la solución más rápida. (operator.iadd es igualmente rápido.)

 introducir descripción de la imagen aquí


El código para reproducir el terreno:

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,
    kernels=[
        forfor, sum_brackets, functools_reduce, functools_reduce_iconcat,
        itertools_chain, numpy_flat, numpy_concatenate
        ],
    n_range=[2**k for k in range(16)],
    logx=True,
    logy=True,
    xlabel='num lists'
    )
from functools import reduce #python 3

>>> 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 método extend() en su ejemplo se modifica x en lugar de devolver un valor útil (que reduce() espera).

Una forma más rápida de hacer la versión reduce 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]

Aquí es un enfoque general que se aplica a números , cadenas , anidados listas y mezclados contenedores.

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 reemplazar for sub_x in flatten(x): yield sub_x
  • En Python 3.8, clases base abstractas movido de collection.abc al módulo typing.

Demostración

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

referencia

  • Esta solución se modificó a partir de una receta en Beazley, D. y B. Jones. Receta 4.14, Python Cookbook 3ª Ed, O'Reilly Media Inc. Sebastopol, CA:.. 2013
  • Encontrados un SO poste , posiblemente la manifestación inicial.

Si desea para aplanar una estructura de datos en la que no sabe qué tan profunda está anidado que podría utilizar 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]

Es un generador de lo que necesita para emitir el resultado a un list o explícitamente iterar sobre ella.


Para acoplar un solo nivel y si cada uno de los elementos es en sí iterables también se puede utilizar iteration_utilities.flatten que en sí es sólo un envoltorio delgado alrededor 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]

Sólo para añadir algunos tiempos de respuesta (en base a Nico Schlömer que no incluían la función presentada en esta respuesta):

 introducir descripción de la imagen aquí

Es un gráfico log-log para dar cabida a la amplia gama de valores abarcaron. Para razonamiento cualitativo: bajo es mejor

.

Los resultados muestran que si el iterable contiene sólo unos pocos iterables interiores continuación sum será más rápido, sin embargo, para iterables largos sólo el itertools.chain.from_iterable, iteration_utilities.deepflatten o la comprensión anidada tienen un rendimiento razonable con itertools.chain.from_iterable ser el más rápido (como ya se ha notado por 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 Exención de responsabilidad: Soy el autor de esa biblioteca

tomo mi declaración posterior. suma no es el ganador. A pesar de que es más rápido cuando la lista es pequeña. Sin embargo, el rendimiento se degrada significativamente con las listas más grandes.

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

La versión suma sigue en funcionamiento durante más de un minuto y no lo ha hecho todavía procesamiento!

Para listas medianas:

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

Uso de pequeñas listas y timeit: número = 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

Parece que hay una confusión con operator.add! Cuando se agrega dos listas, el término correcto para que sea concat, no añade. operator.concat es lo que hay que utilizar.

Si usted está pensando funcional, es tan fácil como esto ::

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

Ves a reducir aspectos, el tipo de secuencia, por lo que cuando se suministra una tupla, a volver una tupla. Vamos a probar con una lista ::

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

Aha, se obtiene una lista de vuelta.

¿Qué tal el rendimiento ::

>>> 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 es bastante rápido! Pero no es una comparación de reducir con 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

¿Por qué usan extender?

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

Esto debería funcionar bien.

Considere instalar el paquete more_itertools .

> pip install more_itertools

Viene con una implementación para flatten ( href="https://more-itertools.readthedocs.io/en/stable/_modules/more_itertools/recipes.html#flatten" , desde el itertools recetas ):

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]

A partir de la versión 2.4, puede aplanarse más complicados iterables, anidadas con more_itertools.collapse ( fuente , aportado 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]

La razón por la que su función no funcionó: la gama se extiende extienden en el lugar y no se devuelve. Todavía se puede volver x desde lambda, usando un truco:

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

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

No reinventar la rueda si está utilizando Django

>>> from django.contrib.admin.utils import flatten
>>> l = [[1,2,3], [4,5], [6]]
>>> flatten(l)
>>> [1, 2, 3, 4, 5, 6]

... pandas

>>> from pandas.core.common import flatten
>>> list(flatten(l))

... itertools

>>> import itertools
>>> flatten = itertools.chain.from_iterable
>>> list(flatten(l))

... Matplotlib

>>> from matplotlib.cbook import flatten
>>> list(flatten(l))

... Unipath

>>> from unipath.path import flatten
>>> list(flatten(l))

... Setuptools

>>> from setuptools.namespaces import flatten
>>> list(flatten(l))
def flatten(l, a):
    for i in l:
        if isinstance(i, list):
            flatten(i, a)
        else:
            a.append(i)
    return a

print(flatten([[[1, [1,1, [3, [4,5,]]]], 2, 3], [4, 5],6], []))

# [1, 1, 1, 3, 4, 5, 2, 3, 4, 5, 6]

Una característica mala de la función de Anil anterior es que requiere que el usuario especifique siempre manualmente el segundo argumento de ser una lista [] vacía. Este lugar debe ser un defecto. Debido a la forma en que funcionan los objetos de Python, éstas deben establecerse dentro de la función, no en los argumentos.

Esto es una función de trabajo:

def list_flatten(l, a=None):
    #check a
    if a is None:
        #initialize with empty list
        a = []

    for i in l:
        if isinstance(i, list):
            list_flatten(i, a)
        else:
            a.append(i)
    return a

Pruebas:

In [2]: lst = [1, 2, [3], [[4]],[5,[6]]]

In [3]: lst
Out[3]: [1, 2, [3], [[4]], [5, [6]]]

In [11]: list_flatten(lst)
Out[11]: [1, 2, 3, 4, 5, 6]

matplotlib.cbook.flatten() trabajará para las listas anidadas aunque nido 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))

Resultados:

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

Este es 18x más rápido que el guión bajo ._ 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

La respuesta aceptada no funcionaba para mí cuando se trata de listas basadas en texto de longitudes variables. Aquí es un enfoque alternativo que hizo el trabajo para mí.

l = ['aaa', 'bb', 'cccccc', ['xx', 'yyyyyyy']]

respuesta aceptada que hizo no de trabajo:

flat_list = [item for sublist in l for item in sublist]
print(flat_list)
['a', 'a', 'a', 'b', 'b', 'c', 'c', 'c', 'c', 'c', 'c', 'xx', 'yyyyyyy']

Nueva propuesta de solución que hizo trabajo para mí:

flat_list = []
_ = [flat_list.extend(item) if isinstance(item, list) else flat_list.append(item) for item in l if item]
print(flat_list)
['aaa', 'bb', 'cccccc', 'xx', 'yyyyyyy']

versión recursiva

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

def flatten_list(k):
    result = list()
    for i in k:
        if isinstance(i,list):

            #The isinstance() function checks if the object (first argument) is an 
            #instance or subclass of classinfo class (second argument)

            result.extend(flatten_list(i)) #Recursive call
        else:
            result.append(i)
    return result

flatten_list(x)
#result = [1,2,3,4,5,6,7,8,9,10]

Después de parecer más simple para mí:

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

También se puede utilizar de NumPy plana :

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

Editar 02/11/2016: Sólo funciona cuando sublistas tienen dimensiones idénticas

.

Puede utilizar numpy:
flat_list = list(np.concatenate(list_of_list))

underscore.py ventilador paquete

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

Se resuelve todos los problemas de aplanar (elemento de la lista ninguno o anidación complejo)

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]

Puede instalar underscore.py con pip

pip install underscore.py
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

Este código también funciona bien, ya que acaba de ampliar la lista de todo el camino. Aunque es mucho más similar pero sólo tienen un bucle for. Por lo tanto, tiene menos complejidad que la adición de 2 bucles.

Si usted está dispuesto a renunciar a una pequeña cantidad de velocidad para un aspecto más limpio, entonces se podría utilizar 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

solución más rápida que he encontrado (para una lista amplia de todos modos):

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

Listo! Por supuesto, puede convertirla de nuevo en una lista por lista de ejecución (l)

Esto puede no ser la forma más eficiente, pero pensé que poner una sola línea (en realidad dos forro). Ambas versiones trabajarán en las listas de jerarquía anidada arbitrarias, y explota las características del lenguaje (Python3.5) y recursión.

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 en una primera forma de profundidad. La recursividad se cae hasta que encuentra un elemento no-lista, a continuación, se extiende la flist variable local y luego revierte a los padres. Cada vez que se devuelve flist, que se extiende a flist de los padres en la lista de la comprensión. Por lo tanto, en la raíz, se devuelve una lista plana.

La de arriba crea varias listas locales y los devuelve que se utiliza para ampliar la lista de los padres. Creo que el camino alrededor de esto puede ser la creación de un flist gloabl, como 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]

A pesar de que no estoy seguro en este momento acerca de la eficiencia.

Nota: : A continuación se aplica a Python 3.3+, ya que utiliza yield_from . six es también un paquete de terceros, aunque es estable. Alternativamente, se puede usar sys.version.


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

Sin embargo, tenga en cuenta este caso ligeramente más complejo:

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

Hay varios problemas aquí:

  • Un elemento, 6, es sólo un escalar; no es iterable, por lo que las vías anteriores se producirá un error aquí.
  • Un elemento, 'abc', es técnicamente iterable (todos son strs). Sin embargo, la lectura entre líneas un poco, que no quiere tratar como tal -. Desea tratarlo como un único elemento
  • El elemento final, [8, [9, 10]] es en sí mismo un iterable anidada. lista por comprensión básica y chain.from_iterable único extracto "1 nivel hacia abajo."

Usted puede remediar esto como sigue:

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

A continuación, se comprueba que el sub-elemento (1) es iterable con Iterable, un ABC de itertools, sino que también quieren asegurarse de que (2) es el elemento no "similar a una cuerda."

Otro enfoque inusual que funcione para hetero y listas homogéneas de números 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(',')]

Un método recursivo simple usando reduce de functools y el operador add en las listas:

>>> from functools import reduce
>>> from operator import add
>>> flatten = lambda lst: [lst] if type(lst) is int else reduce(add, [flatten(ele) for ele in lst])
>>> flatten(l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

El flatten función toma en lst como parámetro. Se bucles de todos los elementos de lst hasta alcanzar números enteros (también puede cambiar int a float, str, etc. para otros tipos de datos), que se añaden al valor de retorno de la recursión más externa.

recursión, a diferencia de métodos como bucles for y mónadas, es que es una solución general no limitada por la lista profundidad . Por ejemplo, una lista con la profundidad de 5 puede ser aplanada de la misma manera como l:

>>> l2 = [[3, [1, 2], [[[6], 5], 4, 0], 7, [[8]], [9, 10]]]
>>> flatten(l2)
[3, 1, 2, 6, 5, 4, 0, 7, 8, 9, 10]
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top