Sí, sé que este tema se ha cubierto antes ( aquí , aquí , aquí , aquí ), pero que yo sepa, todas las soluciones, excepto una, fallan en una lista como esta:
L = [[[1, 2, 3], [4, 5]], 6]
Donde la salida deseada es
[1, 2, 3, 4, 5, 6]
O tal vez incluso mejor, un iterador. La única solución que vi que funciona para un anidamiento arbitrario se encuentra en esta pregunta :
def flatten(x):
result = []
for el in x:
if hasattr(el, "__iter__") and not isinstance(el, basestring):
result.extend(flatten(el))
else:
result.append(el)
return result
flatten(L)
¿Es este el mejor modelo? ¿Pasé por alto algo? Cualquier problema?
python
list
optimization
nested-lists
flatten
telliott99
fuente
fuente
list
s pretenden ser homogéneas) no significa que sea una falla de Python y que necesitamos una construcción para tal tareaRespuestas:
El uso de las funciones del generador puede hacer que su ejemplo sea un poco más fácil de leer y probablemente aumente el rendimiento.
Python 2
Utilicé el Iterable ABC añadió en 2,6.
Python 3
En Python 3,
basestring
ya no existe, pero puedes usar una tupla destr
ybytes
obtener el mismo efecto allí.El
yield from
operador devuelve un artículo de un generador uno a la vez. Esta sintaxis para delegar en un subgenerador se agregó en 3.3fuente
l = ([[chr(i),chr(i-32)] for i in xrange(ord('a'), ord('z')+1)] + range(0,9))
en un instante cuando hice estolist(flatten(l))
. ¡Todos los demás comenzarían a trabajar y tardarían para siempre!collections.Sequence
lugar decollections.Iteratable
?for i in flatten(42): print (i)
. Esto podría solucionarse moviendo laisinstance
cláusulafor el
-test y la cláusula else fuera del bucle. (Entonces podría arrojarle cualquier cosa, y se haría una lista aplanada)collections.Iterable
está en desuso. Usar en sucollections.abc.Iterable
lugar.Mi solución:
Un poco más conciso, pero más o menos lo mismo.
fuente
try: iter(x)
prueba si es iterable ... Pero no creo que tener que importar un módulo stdlib sea una desventaja que vale la pena evitar.int
def flatten(x): return [a for i in x for a in flatten(i)] if isinstance(x, collections.Iterable) else [x]
pero la legibilidad podría ser subjetiva aquí.if isinstance(x, collections.Iterable) and not isinstance(x, basestring)
collections.Iterable
conlist
Generador que utiliza la recursividad y la escritura de pato (actualizado para Python 3)
fuente
for i in flatten(item): yield i
Versión del generador de la solución no recursiva de @unutbu, según lo solicitado por @Andrew en un comentario:
Versión ligeramente simplificada de este generador:
fuente
Aquí está mi versión funcional de aplanar recursivo que maneja tuplas y listas, y le permite agregar cualquier combinación de argumentos posicionales. Devuelve un generador que produce la secuencia completa en orden, arg por arg:
Uso:
fuente
e
,a
,n
se refiere aargs
den
,intermediate
(o la más cortamid
, o si lo prefiereelement
) dea
yresult
parae
, por lo que:flatten = lambda *args: (result for mid in args for result in (flatten(*mid) if isinstance(mid, (tuple, list)) else (mid,)))
compiler.ast.flatten
. Gran código compacto, funciona para cualquier tipo de objeto (creo).Esta versión de
flatten
evita el límite de recursión de Python (y, por lo tanto, funciona con iterables anidados arbitrariamente profundos). Es un generador que puede manejar cadenas e iterables arbitrarios (incluso infinitos).Aquí hay algunos ejemplos que demuestran su uso:
Aunque
flatten
puede manejar generadores infinitos, no puede manejar anidamiento infinito:fuente
sets
,dicts
,deques
,listiterators
,generators
,, Filehandles y clases personalizadas con__iter__
definido son todas las instancias decollections.Iterable
, pero nocollections.Sequence
. El resultado de aplanar adict
es un poco dudoso, pero por lo demás, creo quecollections.Iterable
es un mejor valor predeterminado quecollections.Sequence
. Definitivamente es el más liberal.collections.Iterable
es que esto incluye generadores infinitos. He cambiado mi respuesta manejar este caso.StopIteration
. Además, parece quewhile True: first = next(remainder)
podría ser reemplazado porfor first in remainder:
.try-except StopIteration block
.Aquí hay otra respuesta que es aún más interesante ...
Básicamente, convierte la lista anidada en una cadena, usa una expresión regular para eliminar la sintaxis anidada y luego convierte el resultado nuevamente en una lista (aplanada).
fuente
[['C=64', 'APPLE ]['], ['Amiga', 'Mac', 'ST']]
:) Por otro lado, dada una lista que se contiene, funcionará un poco mejor que las otras respuestas, generando un excepción en lugar de simplemente hacer un bucle hasta que se quede sin memoria / recurrir hasta agotar la pila ...[x for x in c]
es solo una forma lenta y detallada de hacer una copiac
, entonces ¿por qué harías eso? En segundo lugar, su código claramente se convertirá'APPLE ]['
en'APPLE '
, porque no maneja las comillas, solo asume que los corchetes son corchetes de lista.arr_str = str(arr)
y luego[int(s) for s in re.findall(r'\d+', arr_str)]
realmente. Ver github.com/jorgeorpinel/flatten_nested_lists/blob/master/…fuente
Puede usar
deepflatten
desde el paquete de tercerositeration_utilities
:Es un iterador, por lo que debe iterarlo (por ejemplo, envolviéndolo
list
o usándolo en un bucle). Internamente utiliza un enfoque iterativo en lugar de un enfoque recursivo y está escrito como una extensión C, por lo que puede ser más rápido que los enfoques de Python puro:Soy el autor de la
iteration_utilities
biblioteca.fuente
Fue divertido intentar crear una función que pudiera aplanar la lista irregular en Python, pero, por supuesto, para eso está Python (hacer que la programación sea divertida). El siguiente generador funciona bastante bien con algunas advertencias:
Se aplanará tipos de datos que es posible que desee dejar en paz (como
bytearray
,bytes
ystr
objetos). Además, el código se basa en el hecho de que solicitar un iterador de un no iterable aumenta aTypeError
.Editar:
No estoy de acuerdo con la implementación anterior. El problema es que no deberías poder aplanar algo que no sea iterable. Es confuso y da la impresión equivocada del argumento.
El siguiente generador es casi el mismo que el primero pero no tiene el problema de intentar aplanar un objeto no iterable. Falla como cabría esperar cuando se le da un argumento inapropiado.
Probar el generador funciona bien con la lista que se proporcionó. Sin embargo, el nuevo código generará un
TypeError
cuando se le da un objeto no iterable. A continuación se muestran ejemplos del nuevo comportamiento.fuente
Aunque se ha seleccionado una respuesta elegante y muy pitónica, presentaría mi solución solo para la revisión:
Por favor diga qué tan bueno o malo es este código?
fuente
isinstance(i, (tuple, list))
. La inicialización de las variables vacía es una bandera para que se ocupara de estructuras alternativas de código, típicamente por comprensión, generadores, recursividad, etc.return type(l)(ret)
también le devolverá el mismo tipo de contenedor que se le pasó. :)Prefiero respuestas simples. No hay generadores. Sin recursión o límites de recursión. Solo iteración:
Esto funciona con dos listas: un bucle for interno y un bucle while externo.
El bucle for interno itera a través de la lista. Si encuentra un elemento de lista, (1) usa list.extend () para aplanar esa parte de un nivel de anidamiento y (2) cambia keepChecking a True. keepchecking se utiliza para controlar el bucle while externo. Si el bucle externo se establece en verdadero, activa el bucle interno para otro pase.
Esos pases continúan sucediendo hasta que no se encuentren más listas anidadas. Cuando finalmente se produce un pase donde no se encuentra ninguno, keepChecking nunca se dispara a verdadero, lo que significa que listIsNested permanece falso y el ciclo externo sale.
La lista aplanada se devuelve.
Prueba de funcionamiento
[1, 2, 3, 4, 100, 200, 300, 1000, 2000, 3000]
fuente
Aquí hay una función simple que aplana las listas de profundidad arbitraria. Sin recursividad, para evitar el desbordamiento de la pila.
fuente
Me sorprende que nadie haya pensado en esto. Maldita recursión No obtengo las respuestas recursivas que hicieron las personas avanzadas aquí. de todos modos aquí está mi intento de esto. La advertencia es que es muy específica para el caso de uso del OP
salida:
fuente
No revisé todas las respuestas ya disponibles aquí, pero aquí hay una frase que se me ocurrió, tomando prestado de la forma en que Lisp procesó primero y de la lista de descanso
Aquí hay un caso simple y uno no tan simple:
fuente
def foo():
hay una línea separada. Además, esto es muy ilegible.Al intentar responder a una pregunta de este tipo, realmente necesita dar las limitaciones del código que propone como solución. Si solo se tratara de actuaciones, no me importaría demasiado, pero la mayoría de los códigos propuestos como solución (incluida la respuesta aceptada) no logran aplanar ninguna lista que tenga una profundidad superior a 1000.
Cuando digo la mayoría de los códigos, me refiero a todos los códigos que usan cualquier forma de recursión (o llaman a una función de biblioteca estándar que es recursiva). Todos estos códigos fallan porque por cada llamada recursiva realizada, la pila (llamada) crece en una unidad, y la pila de llamadas python (predeterminada) tiene un tamaño de 1000.
Si no está demasiado familiarizado con la pila de llamadas, quizás lo siguiente le ayude (de lo contrario, puede desplazarse a la Implementación ).
Tamaño de la pila de llamadas y programación recursiva (analogía de mazmorra)
Encontrar el tesoro y salir
Imagina que entras en una mazmorra enorme con habitaciones numeradas , buscando un tesoro. No conoce el lugar, pero tiene algunas indicaciones sobre cómo encontrar el tesoro. Cada indicación es un acertijo (la dificultad varía, pero no se puede predecir qué tan difícil será). Decides pensar un poco en una estrategia para ahorrar tiempo, haces dos observaciones:
Al entrar en la mazmorra, ves un pequeño cuaderno aquí. Decide usarlo para escribir cada habitación que salga después de resolver un acertijo (al ingresar a una nueva habitación), de esta manera podrá regresar a la entrada. Esa es una idea genial, ni siquiera gastará un centavo implementando su estrategia.
Entras al calabozo, resolviendo con gran éxito los primeros 1001 acertijos, pero aquí viene algo que no habías planeado, no te queda espacio en el cuaderno que tomaste prestado. Decides abandonar tu búsqueda, ya que prefieres no tener el tesoro que perderte para siempre dentro de la mazmorra (eso parece inteligente).
Ejecutando un programa recursivo
Básicamente, es exactamente lo mismo que encontrar el tesoro. El calabozo es la memoria de la computadora , su objetivo ahora no es encontrar un tesoro sino calcular alguna función (encontrar f (x) para una x dada ). Las indicaciones simplemente son subrutinas que lo ayudarán a resolver f (x) . Su estrategia es la misma que la estrategia de la pila de llamadas , el cuaderno es la pila, las salas son las direcciones de retorno de las funciones:
El problema que encontró en la mazmorra será el mismo aquí, la pila de llamadas tiene un tamaño finito (aquí 1000) y, por lo tanto, si ingresa demasiadas funciones sin regresar, completará la pila de llamadas y tendrá un error que se verá como
"Querido aventurero, lo siento mucho, pero su cuaderno está lleno":RecursionError: maximum recursion depth exceeded
. Tenga en cuenta que no necesita recursividad para llenar la pila de llamadas, pero es muy poco probable que un programa no recursivo llame a 1000 funciones sin volver nunca. También es importante comprender que una vez que regresó de una función, la pila de llamadas se libera de la dirección utilizada (de ahí el nombre "pila", la dirección de retorno se inserta antes de ingresar una función y se retira al regresar). En el caso especial de una recursión simple (una funciónf
que se llama a sí mismo una vez, una y otra vez, entraráf
una y otra vez hasta que finalice el cálculo (hasta que se encuentre el tesoro) y regresaráf
hasta que regrese al lugar donde llamóf
en primer lugar. La pila de llamadas nunca se liberará de nada hasta el final, donde se liberará de todas las direcciones de retorno, una tras otra.¿Cómo evitar este problema?
En realidad, eso es bastante simple: "no uses la recursividad si no sabes qué tan profundo puede llegar". Eso no siempre es cierto, ya que en algunos casos, la recursividad de llamadas de cola se puede optimizar (TCO) . Pero en Python, este no es el caso, e incluso la función recursiva "bien escrita" no optimizará el uso de la pila. Hay una publicación interesante de Guido sobre esta pregunta: Eliminación de recursión de cola .
Hay una técnica que puede usar para hacer que cualquier función recursiva sea iterativa, esta técnica podríamos llamar traer su propio cuaderno . Por ejemplo, en nuestro caso particular simplemente estamos explorando una lista, ingresar a una habitación es equivalente a ingresar una sublista, la pregunta que debe hacerse es cómo puedo volver de una lista a su lista principal. La respuesta no es tan compleja, repita lo siguiente hasta que
stack
esté vacío:address
yindex
en unstack
al ingresar una nueva sublista (tenga en cuenta que una dirección de lista + índice también es una dirección, por lo tanto, solo usamos la misma técnica utilizada por la pila de llamadas);yield
(o agréguelo a una lista);stack
retornoaddress
(yindex
) .También tenga en cuenta que esto es equivalente a un DFS en un árbol donde algunos nodos son sublistas
A = [1, 2]
y algunos son elementos simples:0, 1, 2, 3, 4
(paraL = [0, [1,2], 3, 4]
). El árbol se ve así:El preorden transversal de DFS es: L, 0, A, 1, 2, 3, 4. Recuerde, para implementar un DFS iterativo también "necesita" una pila. La implementación que propuse antes da como resultado tener los siguientes estados (para el
stack
y elflat_list
):En este ejemplo, el tamaño máximo de la pila es 2, porque la lista de entrada (y, por lo tanto, el árbol) tiene profundidad 2.
Implementación
Para la implementación, en python puede simplificar un poco utilizando iteradores en lugar de listas simples. Las referencias a los (sub) iteradores se utilizarán para almacenar las direcciones de retorno de las sublistas (en lugar de tener tanto la dirección de la lista como el índice). Esto no es una gran diferencia, pero creo que es más legible (y también un poco más rápido):
Además,
is_list_like
tenga en cuenta que en I haveisinstance(item, list)
, que podría cambiarse para manejar más tipos de entrada, aquí solo quería tener la versión más simple donde (iterable) es solo una lista. Pero también podrías hacer eso:Esto considera las cadenas como "elementos simples" y, por
flatten_iter([["test", "a"], "b])
lo tanto , regresará["test", "a", "b"]
y no["t", "e", "s", "t", "a", "b"]
. Tenga en cuenta que en ese caso,iter(item)
se llama dos veces en cada elemento, supongamos que es un ejercicio para el lector hacer esto más limpio.Pruebas y comentarios sobre otras implementaciones
Al final, recuerde que usted no puede imprimir una lista infinitamente anidada
L
usandoprint(L)
, porque internamente se utilizará para llamadas recursivas__repr__
(RecursionError: maximum recursion depth exceeded while getting the repr of an object
). Por la misma razón, las soluciones a laflatten
participaciónstr
fallarán con el mismo mensaje de error.Si necesita probar su solución, puede usar esta función para generar una lista anidada simple:
Lo que da:
build_deep_list(5)
>>>[4, [3, [2, [1, [0]]]]]
.fuente
Aquí está la
compiler.ast.flatten
implementación en 2.7.5:Hay métodos mejores y más rápidos (si has llegado hasta aquí, ya los has visto)
También tenga en cuenta:
fuente
totalmente hacky pero creo que funcionaría (dependiendo de su tipo de datos)
fuente
Solo usa una
funcy
biblioteca:pip install funcy
fuente
Aquí hay otro enfoque de py2, no estoy seguro de si es el más rápido o el más elegante ni el más seguro ...
Puede ignorar cualquier tipo específico (o derivado) que desee, devuelve un iterador, por lo que puede convertirlo a cualquier contenedor específico como list, tuple, dict o simplemente consumirlo para reducir la huella de memoria, para bien o para mal puede manejar objetos iniciales no iterables como int ...
Tenga en cuenta que la mayor parte del trabajo pesado se realiza en C, ya que, por lo que sé, así es como se implementan las herramientas de iterto, por lo tanto, aunque es recursivo, AFAIK no está limitado por la profundidad de recursión de Python ya que las llamadas a funciones están sucediendo en C, aunque esto no significa que esté limitado por la memoria, especialmente en OS X, donde su tamaño de pila tiene un límite estricto a día de hoy (OS X Mavericks) ...
hay un enfoque un poco más rápido, pero un método menos portátil, solo úselo si puede suponer que los elementos base de la entrada se pueden determinar explícitamente de lo contrario, obtendrá una recursión infinita y OS X con su tamaño de pila limitado, lanzar una falla de segmentación bastante rápido ...
aquí estamos usando conjuntos para verificar el tipo, por lo que se necesita O (1) vs O (número de tipos) para verificar si un elemento debe ser ignorado o no, aunque, por supuesto, cualquier valor con el tipo derivado de los tipos ignorados declarados fallará , esta es la razón por la que está usando
str
,unicode
así que úselo con precaución ...pruebas:
fuente
Sin usar ninguna biblioteca:
fuente
Utilizando
itertools.chain
:O sin encadenar:
fuente
Solía recursivo para resolver la lista anidada con cualquier profundidad
Entonces, después de definir la función combine_nlist, es fácil usar esta función para aplanar. O puede combinarlo en una función. Me gusta mi solución porque se puede aplicar a cualquier lista anidada.
resultado
fuente
current_value = combiner(current_value,each_item) RecursionError: maximum recursion depth exceeded
La forma más fácil es usar la biblioteca morph usando
pip install morph
.El codigo es:
fuente
Soy consciente de que ya hay muchas respuestas increíbles, pero quería agregar una respuesta que utilice el método de programación funcional para resolver la pregunta. En esta respuesta, uso la doble recursión:
salida:
fuente
No estoy seguro de si esto es necesariamente más rápido o más efectivo, pero esto es lo que hago:
La
flatten
función aquí convierte la lista en una cadena, saca todos los corchetes, vuelve a colocar los corchetes en los extremos y los convierte nuevamente en una lista.Sin embargo, si supiera que tendría corchetes en su lista en cadenas, como
[[1, 2], "[3, 4] and [5]"]
, tendría que hacer algo más.fuente
Esta es una implementación simple de flatten en python2
fuente
Esto aplanará una lista o diccionario (o una lista de listas o diccionarios de diccionarios, etc.). Se supone que los valores son cadenas y crea una cadena que concatena cada elemento con un argumento separador. Si lo desea, puede usar el separador para dividir el resultado en un objeto de lista después. Utiliza la recursividad si el siguiente valor es una lista o una cadena. Use el argumento clave para saber si desea las claves o los valores (establezca la clave en falso) del objeto del diccionario.
rendimientos:
fuente
Si le gusta la recursividad, esta podría ser una solución de su interés:
De hecho, adapté esto de algún código de esquema de práctica que había escrito hace un tiempo.
¡Disfrutar!
fuente
Soy nuevo en Python y vengo de un fondo lisp. Esto es lo que se me ocurrió (mira los nombres de var para lulz):
Parece funcionar. Prueba:
devoluciones:
fuente