¿Por qué Python no tiene una función de "aplanar" para las listas?

39

Erlang y Ruby vienen con funciones para aplanar matrices. Parece una herramienta tan simple y útil para agregar a un idioma. Uno podría hacer esto:

>>> mess = [[1, [2]], 3, [[[4, 5]], 6]]
>>> mess.flatten()
[1, 2, 3, 4, 5, 6]

O incluso:

>>> import itertools
>>> mess = [[1, [2]], 3, [[[4, 5]], 6]]
>>> list(itertools.flatten(mess))
[1, 2, 3, 4, 5, 6]

En cambio, en Python, uno tiene que pasar por la molestia de escribir una función para aplanar matrices desde cero. Esto me parece tonto, aplanar matrices es algo muy común. Es como tener que escribir una función personalizada para concatenar dos matrices.

He buscado en Google esto infructuosamente, así que pregunto aquí; ¿Hay alguna razón particular por la cual un lenguaje maduro como Python 3, que viene con cientos de miles de baterías diferentes incluidas, no proporcione un método simple para aplanar matrices? ¿Se ha discutido y rechazado la idea de incluir tal función en algún momento?

Hubro
fuente
2
@detly: Pasé por alto el aplanamiento últimamente cuando utilicé varias consultas para recuperar datos de diferentes fuentes. Cada consulta devuelve una lista de diccionarios, por lo que al final tengo una lista de listas de diccionarios para convertir en una lista de diccionarios. Utilicé un bucle +, extendpero aplanar habría sido mucho más elegante. Sin embargo, me equivoco si este patrón es lo suficientemente común como para justificar haber aplanado en la biblioteca estándar.
Giorgio
44
"Quiero decir, imagina que si introduces un error en tu código que cambia involuntariamente la estructura de tus datos. Flatten seguirá funcionando, pero producirá resultados completamente incorrectos": esta es una de las razones por las que me gustan los lenguajes de tipo estático. ;-)
Giorgio
2
@BryanOakley Vea el comentario anterior también (aunque no para listas de varios niveles, el aplanamiento en general es común)
Izkata
3
Está integrado en Mathemaica, y lo uso ampliamente.
Según Alexandersson el

Respuestas:

34

Las propuestas para flattenagregar una función a la biblioteca estándar aparecen de vez en cuando en las listas de correo python-dev y python-ideas . Los desarrolladores de Python suelen responder con los siguientes puntos:

  1. Un aplanamiento de un nivel (que convierte un iterable de iterables en un único iterable) es una expresión trivial de una línea (x for y in z for x in y)y, en cualquier caso, ya está en la biblioteca estándar bajo el nombre itertools.chain.from_iterable.

  2. ¿Cuáles son los casos de uso para un aplanamiento multinivel de uso general? ¿Son realmente lo suficientemente convincentes como para que la función se agregue a la biblioteca estándar?

  3. ¿Cómo decidiría un aplanamiento multinivel de propósito general cuándo aplanar y cuándo dejar solo? Puede pensar que una regla como "aplanar cualquier cosa que admita la interfaz iterable" funcionaría, pero eso conduciría a un bucle infinito flatten('a').

Ver por ejemplo Raymond Hettinger :

Se ha discutido ad nauseam en comp.lang.python. Las personas parecen disfrutar escribiendo sus propias versiones de flatten más que encontrar casos de uso legítimos que aún no tienen soluciones triviales.

Un aplanador de uso general necesita alguna forma de saber qué es atómico y qué puede subdividirse aún más. Además, no es obvio cómo se debe extender el algoritmo para cubrir las entradas con estructuras de datos en forma de árbol con datos en los nodos, así como las hojas (preorden, postorder, transversal, etc.)

Gareth Rees
fuente
Para ser explícito, esto significa que la función de un nivel flattense puede definir como lambda z: [x for y in z for x in y].
Christopher Martin
1
"Un aplanador de uso general necesita alguna forma de saber qué es atómico y qué se puede subdividir aún más". Esto suena como un problema que se puede resolver usando OOP: cada objeto podría tener un flattenmétodo. La implementación de este método debe recurrir recursivamente flattena su subcomponente, si el objeto es un compuesto. Desafortunadamente, AFAIK no todos los valores son un objeto en Python. Sin embargo, en Ruby debería funcionar.
Giorgio
1
un ayudante de aplanamiento para un aplanamiento de un nivel en lugar de un continuo "para adentro para adentro" ya es un caso suficientemente bueno de la OMI. fácilmente legible
dtc
2
@Giorgio Python evita estos métodos. Se prefieren los protocolos, y creo que son mucho más fáciles de trabajar que un diseño OOP, ya que a menudo ni siquiera necesita implementar mucho.
jpmc26
8

Viene con tal método pero no lo llama aplanar. Se llama " cadena ". Devuelve un iterador en el que luego deberías usar la función list () para volver a convertirlo en una lista. Si no desea usar un *, puede usar la segunda versión "from_iterator". Funciona igual en Python 3. Fallará si la entrada de la lista no es una lista de listas.

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

Hubo una vez un método de aplanamiento en el módulo compiler.ast, pero esto fue desaprobado en 2.6 y luego eliminado en 3.0. La recursividad de profundidad arbitraria, necesaria para listas anidadas arbitrariamente, no funciona bien con la profundidad de recursión máxima conservadora de Python. El razonamiento para la eliminación del compilador se debió en gran medida a que era un desastre . El compilador se convirtió en ast, pero se quedó atrás.

La profundidad arbitraria se puede lograr con las matrices de numpy y el aplanamiento de esa biblioteca.

Ingeniero mundial
fuente
La chain.from_iteratorfunción, como dijiste, solo se puede usar para aplanar listas bidimensionales. Una función realmente plana , que acepta cualquier cantidad de listas anidadas y devuelve una lista unidimensional, aún sería enormemente útil en muchos casos (al menos en mi opinión)
Hubro
2
@Hubro: "en muchos casos", ¿puedes nombrar seis?
Gareth Rees
1
@GarethRees: di algunos ejemplos aquí: programmers.stackexchange.com/questions/254279/…
Hubro el
También iría tan lejos como para argumentar que si este otro lenguaje de hecho proporciona una característica para aplanar una lista de la manera muy simple descrita, ese es uno de los argumentos más convincentes en apoyo de agregar esa habilidad simple a Python.
Bobort
¿Devuelve un iterador o un generador?
jpmc26
-1

... tal vez porque no es tan difícil escribir uno tú mismo

def flatten(l): return flatten(l[0]) + (flatten(l[1:]) if len(l) > 1 else []) if type(l) is list else [l]

... y luego aplanar todo lo que quieras :)

>>> flatten([1,[2,3],4])
[1, 2, 3, 4]
>>> flatten([1, [2, 3], 4, [5, [6, {'name': 'some_name', 'age':30}, 7]], [8, 9, [10, [11, [12, [13, {'some', 'set'}, 14, [15, 'some_string'], 16], 17, 18], 19], 20], 21, 22, [23, 24], 25], 26, 27, 28, 29, 30])
[1, 2, 3, 4, 5, 6, {'age': 30, 'name': 'some_name'}, 7, 8, 9, 10, 11, 12, 13, set(['set', 'some']), 14, 15, 'some_string', 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
>>> 
Shreyas
fuente
8
Asker es consciente de eso: "en Python, uno tiene que pasar por la molestia de escribir una función para aplanar matrices desde cero". Esto ni siquiera intenta responder a la pregunta formulada: "Esto me parece una tontería, aplanar matrices es algo muy común. Es como tener que escribir una función personalizada para concatenar dos matrices".
mosquito
1
Fuera de tema ... Pero super genial :-) !!
SeF
esta respuesta es como decirle a OP que no es un buen desarrollador porque no sabía cómo codificar la función él mismo. Le sugiero que modifique el comienzo de su respuesta porque este es un código útil para aquellos que tropiezan con la pregunta, incluso si están fuera de tema
Federico Bonelli