Reemplazo de Python 3 para la comstackción obsoleta. Aplanar la función

¿Cuál es la forma recomendada para aplanar las listas anidadas desde la desaprobación del paquete del comstackdor ?

>>> from compiler.ast import flatten >>> flatten(["junk",["nested stuff"],[],[[]]]) ['junk', 'nested stuff'] 

Sé que hay algunas respuestas de desbordamiento de stack para el aplanamiento de listas, pero estoy esperando que el paquete estándar y “pythonic” sea una forma obvia y obvia de hacerlo.

Related of "Reemplazo de Python 3 para la comstackción obsoleta. Aplanar la función"

Su función declarada toma una lista anidada y la aplana en una nueva lista.

Para aplanar una lista anidada arbitrariamente en una nueva lista, esto funciona en Python 3 como espera:

 import collections def flatten(x): result = [] for el in x: if isinstance(x, collections.Iterable) and not isinstance(el, str): result.extend(flatten(el)) else: result.append(el) return result print(flatten(["junk",["nested stuff"],[],[[]]])) 

Huellas dactilares:

 ['junk', 'nested stuff'] 

Si quieres un generador que haga lo mismo:

 def flat_gen(x): def iselement(e): return not(isinstance(e, collections.Iterable) and not isinstance(e, str)) for el in x: if iselement(el): yield el else: for sub in flat_gen(el): yield sub print(list(flat_gen(["junk",["nested stuff"],[],[[[],['deep']]]]))) # ['junk', 'nested stuff', 'deep'] 

Para Python 3.3 y versiones posteriores, use el rendimiento en lugar del bucle:

 def flat_gen(x): def iselement(e): return not(isinstance(e, collections.Iterable) and not isinstance(e, str)) for el in x: if iselement(el): yield el else: yield from flat_gen(el) 

itertools.chain es la mejor solución para aplanar cualquier nivel nested iterable, es altamente eficiente en comparación con cualquier solución de python puro.

Dicho esto, funcionará en todos los iterables, por lo que es necesario realizar algunas comprobaciones si desea evitar el aplanamiento de las cadenas, por ejemplo.

Del mismo modo, no se aplanará mágicamente a una profundidad arbitraria. Dicho esto, en general, no se requiere una solución tan genérica; en su lugar, es mejor mantener sus datos estructurados para que no requieran un aplanamiento de esa manera.

Edit: Yo diría que si uno tuviera que hacer un aplanamiento arbitrario, esta es la mejor manera:

 import collections def flatten(iterable): for el in iterable: if isinstance(el, collections.Iterable) and not isinstance(el, str): yield from flatten(el) else: yield el 

Recuerde usar la base de basestring en 2.x sobre str , y for subel in flatten(el): yield el lugar del yield from flatten(el) anterior a 3.3.

Como se señaló en los comentarios, yo diría que esta es la opción nuclear, y es probable que cause más problemas de los que resuelve. En su lugar, la mejor idea es hacer que su salida sea más regular (por ejemplo, la salida que contiene un elemento aún le da como una tupla de un elemento), y realizar un aplanamiento regular en un nivel en el que se introduce, en lugar de hacerlo al final.

Esto producirá más código lógico, legible y más fácil de trabajar. Naturalmente, hay casos en los que necesita hacer este tipo de aplanamiento (si los datos provienen de un lugar con el que no puede meterse, por lo que no tiene más remedio que tomarlos en un formato mal estructurado), en cuyo caso, este tipo de solución podría ser necesaria, pero en general, probablemente sea una mala idea.

Puedes usar la función de aplanar de la librería funcy :

 from funcy import flatten, isa flat_list = flatten(your_list) 

También puede especificar explícitamente qué valores seguir:

 # Follow only sets flat_list = flatten(your_list, follow=isa(set)) 

Eche un vistazo a su implementación si desea un algoritmo.

Mi solución de while-chain fea, solo por diversión:

 from collections import Iterable from itertools import chain def flatten3(seq, exclude=(str,)): sub = iter(seq) try: while sub: while True: j = next(sub) if not isinstance(j, Iterable) or isinstance(j, exclude): yield j else: sub = chain(j, sub) break except StopIteration: return 

No hay un método incorporado para una lista con anidación arbitraria, pero algo como esto …

 def flatten(l): for i in l: if isinstance(i, (list, tuple)): for ii in flatten(i): yield ii else: yield i >>> l = ["junk",["nested stuff"],[],[[]]] >>> list(flatten(l)) ['junk', 'nested stuff'] 

… trabajará para listas y tuplas. Si desea admitir cualquier objeto que pueda usar en un for item in object expresión de for item in object , entonces probablemente sea mejor usar la escritura de pato de esta manera …

 def flatten(l): for i in l: if isinstance(i, (str, bytes)): yield i else: try: for ii in flatten(i): yield ii except TypeError: yield i >>> l = ["junk",["nested stuff"],[],[[]]] >>> list(flatten(l)) ['junk', 'nested stuff'] 

… que es un poco más robusto que la comprobación de la isinstance(el, Iterable) , ya que no se puede hacer frente a algunos casos, como este …

 class Range10: def __getitem__(self, index): if index >= 10: raise IndexError return index >>> import collections >>> r10 = Range10() >>> isinstance(r10, collections.Iterable) False >>> list(Range10()) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 

Construido para la recursión: Python 3.6

 def flatten(lst): """Flattens a list of lists""" return [subelem for elem in lst for subelem in elem] 

Defina sus tipos en una lista y use los elementos incorporados para verificar