steve cooper
tengo una lista A
y una función f
que toma un elemento de A
y devuelve una lista. Puedo usar una lista de comprensión para convertir todo en A
me gusta [f(a) for a in A]
, pero esto devuelve una lista de listas. Supongamos que mi entrada es [a1,a2,a3]
Resultando en [[b11,b12],[b21,b22],[b31,b32]]
.
¿Cómo puedo obtener el aplanado lista [b11,b12,b21,b22,b31,b32]
¿en cambio? En otras palabras, en Python, ¿cómo puedo obtener lo que tradicionalmente se llama flatmap
en lenguajes de programación funcionales, o SelectMany
¿en la red?
(En el código real, A
es una lista de directorios, y f
es os.listdir
. Quiero construir una lista plana de subdirectorios.)
Ver también: ¿Cómo hago una lista plana a partir de una lista de listas? para el problema más general de aplanar una lista de listas después de haberla creado.
Hormigas Asma
Puede tener iteraciones anidadas en una sola lista de comprensión:
[filename for path in dirs for filename in os.listdir(path)]
que es equivalente (al menos funcionalmente) a:
filenames = []
for path in dirs:
for filename in os.listdir(path):
filenames.append(filename)
-
Aunque inteligente, eso es difícil de entender y no muy legible.
– Curtis Yallop
20/10/2014 a las 23:01
-
Realmente no responde la pregunta como se le preguntó. Esto es más bien una solución para no encontrar el problema en primer lugar. ¿Qué pasa si ya tienes una lista de listas? Por ejemplo, ¿qué pasa si su lista de listas es el resultado de la función de mapa del módulo de multiprocesamiento? Quizás la solución de itertools o la solución de reducción sea la mejor.
– Dave31415
22 de enero de 2015 a las 3:43
-
Dave31415:
[ item for list in listoflists for item in list ]
– rampanion
06/02/2015 a las 19:53
-
la ‘legibilidad’ es un juicio subjetivo. Encuentro esta solución bastante legible.
– Cabaña Reb
22 mayo 2015 a las 23:13
-
También pensé que era legible, hasta que vi el orden de los términos… 🙁
– cz
10 mayo 2017 a las 15:14
Julian
>>> from functools import reduce # not needed on Python 2
>>> list_of_lists = [[1, 2],[3, 4, 5], [6]]
>>> reduce(list.__add__, list_of_lists)
[1, 2, 3, 4, 5, 6]
los itertools
La solución es más eficiente, pero esto se siente muy pitónico.
-
Para una lista de 1000 sublistas, esto es 100 veces más lento que el
itertools
manera y la diferencia solo empeora a medida que crece su lista.– Boris Verjovskiy
7 de mayo a las 7:54
-
@BorisV: gracias por comparar eso. ¡No me sorprende! Esta versión probablemente esté bien para unas pocas docenas de sublistas, pero probablemente no debería usarse si el tamaño de entrada no tiene límites (o es grande).
– Julian
10 de mayo a las 14:17
robar
Puedes encontrar una buena respuesta en el itertools
recetas:
import itertools
def flatten(list_of_lists):
return list(itertools.chain.from_iterable(list_of_lists))
-
Se puede usar el mismo enfoque para definir el mapa plano, como se propone en esta respuesta y esta entrada de blog externo
– Josías Yoder
11/07/2017 a las 19:53
Wai Yip Tung
La pregunta propuesta flatmap
. Se proponen algunas implementaciones pero pueden ser innecesarias creando listas intermedias. Aquí hay una implementación que se basa en iteradores.
def flatmap(func, *iterable):
return itertools.chain.from_iterable(map(func, *iterable))
In [148]: list(flatmap(os.listdir, ['c:/mfg','c:/Intel']))
Out[148]: ['SPEC.pdf', 'W7ADD64EN006.cdr', 'W7ADD64EN006.pdf', 'ExtremeGraphics', 'Logs']
En Python 2.x, use itertools.map
en lugar de map
.
Podrías simplemente hacer lo sencillo:
subs = []
for d in dirs:
subs.extend(os.listdir(d))
-
Sí, está bien (aunque no tan bueno como el de @Ants), ¡así que le doy un +1 para honrar su simplicidad!
– Alex Martelli
3 de julio de 2009 a las 1:31
Comunidad
Puede concatenar listas usando el operador de suma normal:
>>> [1, 2] + [3, 4]
[1, 2, 3, 4]
La función incorporada sum
agregará los números en una secuencia y, opcionalmente, puede comenzar desde un valor específico:
>>> sum(xrange(10), 100)
145
Combine lo anterior para aplanar una lista de listas:
>>> sum([[1, 2], [3, 4]], [])
[1, 2, 3, 4]
Ahora puede definir su flatmap
:
>>> def flatmap(f, seq):
... return sum([f(s) for s in seq], [])
...
>>> flatmap(range, [1,2,3])
[0, 0, 1, 0, 1, 2]
Editar: Acabo de ver la crítica en los comentarios para otra respuesta y supongo que es correcto que Python construya innecesariamente y recolecte basura muchas listas más pequeñas con esta solución. Entonces, lo mejor que se puede decir al respecto es que es muy simple y conciso si estás acostumbrado a la programación funcional 🙂
-
Sí, está bien (aunque no tan bueno como el de @Ants), ¡así que le doy un +1 para honrar su simplicidad!
– Alex Martelli
3 de julio de 2009 a las 1:31
subs = []
map(subs.extend, (os.listdir(d) for d in dirs))
(pero la respuesta de Ants es mejor; +1 para él)
-
Usar reduce (o sum, lo que le ahorra muchos caracteres y una importación 😉 para esto es simplemente incorrecto: sigue desechando inútilmente listas viejas para hacer una nueva para cada d. @Ants tiene la respuesta correcta (¡inteligencia de @Steve para aceptarla!).
– Alex Martelli
3 de julio de 2009 a las 1:28
-
No se puede decir en general que esta es una mala solución. Depende de si el rendimiento es incluso un problema. Simple es mejor a menos que haya una razón para optimizar. Es por eso que el método de reducción podría ser mejor para muchos problemas. Por ejemplo, tiene una función lenta que produce una lista de unos pocos cientos de objetos. Desea acelerarlo utilizando la función ‘mapa’ de multiprocesamiento. Entonces crea 4 procesos y usa reduce para mapearlos planamente. En este caso, la función de reducción está bien y es muy legible. Dicho esto, es bueno que señale por qué esto puede ser subóptimo. Pero no siempre es subóptimo.
– Dave31415
22 de enero de 2015 a las 3:48