2010-06-11 15 views
20

Supongamos que tengo una función como esta:concatenación de muchas listas en Python

def getNeighbors(vertex) 

que devuelve una lista de vértices que son vecinos del vértice dado. Ahora quiero crear una lista con todos los vecinos de los vecinos. Lo hago así:

listOfNeighborsNeighbors = [] 
for neighborVertex in getNeighbors(vertex): 
    listOfNeighborsNeighbors.append(getNeighbors(neighborsVertex)) 

¿Hay una manera más pitónica de hacer eso?

Respuesta

22
[x for n in getNeighbors(vertex) for x in getNeighbors(n)] 

o

sum(getNeighbors(n) for n in getNeighbors(vertex), []) 
+0

+1 Iba a sugerir una lista de comprensión. En mi humilde opinión, es la forma más pitónica. –

+1

Sin embargo, consulte las comparaciones de tiempo, como comentarios bajo la respuesta de emu: tanto "itertools.chain" como "reduce (iadd" son más del doble de rápido que la comprensión de la lista anidada, y MUCHO más rápido que sum(), que se degrada rápidamente con # elementos procesados. – ToolmakerSteve

19

Al añadir listas se puede hacer con + y suma():

>>> c = [[1, 2], [3, 4]] 
>>> sum(c, []) 
[1, 2, 3, 4] 
+0

¡Gracias - Yo * sabía * que tenía que haber alguna manera de hacer esto con la suma!Por cierto, no estaba claro para mí que esto funcionaría con más de 2 sublistas o listas de longitud variable; un ejemplo más claro podría ser: 'c = [[1, 2], [3, 4, 5], [6, 7]]' => '[1, 2, 3, 4, 5, 6, 7]' – ToolmakerSteve

+4

PERO vea los tiempos que hice como comentarios bajo la respuesta de emu. ** NO USE SUM - MUY LENTO ** ¡PARA 100 listas de 100 artículos! – ToolmakerSteve

+0

falla para mí: '' l = [[3, 4, 5], [5, 6, 7], [6, 7, 8]] '' -> '' sum (l2, []) '' - > '' TypeError: se requiere un entero'': ¿podría ser un problema según la versión de Python? Estoy usando 2.7.6 – Zak

31

Como de costumbre, el módulo itertools contiene una solución:

>>> l1=[1, 2, 3] 

>>> l2=[4, 5, 6] 

>>> l3=[7, 8, 9] 

>>> import itertools 

>>> list(itertools.chain(l1, l2, l3)) 
[1, 2, 3, 4, 5, 6, 7, 8, 9] 
10

Si la velocidad es importante, puede ser mejor usar esto:

from operator import iadd 
reduce(iadd, (getNeighbors(n) for n in getNeighbors(vertex))) 

El punto de este código está en la concatenación de toda listas por list.extend donde comprensión de lista añadiría un elemento por uno, como si llamar list.append. Eso ahorra un poco de sobrecarga, haciendo que la primera (de acuerdo con mis mediciones) sea aproximadamente tres veces más rápida. (El operador iadd se escribe normalmente como += y hace lo mismo que list.extend.)

El uso de listas por comprensión (la primera solución de Ignacio) sigue siendo por lo general la manera correcta, es más fácil de leer.

Pero definitivamente evite usar sum(..., []), porque se ejecuta en tiempo cuadrático. Eso es muy poco práctico para muchas listas (más de un centenar más o menos).

+0

Gracias por el rendimiento del comentario re sum. Me gustó lo compacto que es ese código, tan bueno saber que no lo usaré a gran escala. En mi humilde opinión, la solución de cadena de itertools de Jochen del '10 es una solución más apropiada que la reducción: más directamente/simplemente hace lo que se solicita. – ToolmakerSteve

+0

Retiro lo que dije acerca de que la cadena es más apropiada. Si bien me resulta más fácil de leer/entender, tiene un ligero problema de rendimiento, si el objetivo final es hacer una lista: pasa los elementos uno a la vez, por lo que la lista no puede predecir cuánto tiempo será. En ese sentido, es más similar a la lista de comprensión/agregar que a reducir/extender. Con timeit, para 100 listas x 100 elementos cada una, reduzca/iadd vs chain aproximadamente el mismo tiempo (1.1). Con 4x de datos (200 listas x 200 elementos cada uno), reduzca las ganancias (2.6 vs 4.0). – ToolmakerSteve

+1

ADVERTENCIA: iadd MODIFICA la primera lista que se pasa. No importa en el ejemplo, porque las listas son resultados de una función. Pero hice una prueba en la que pasé en una lista de listas que había precalculado. Alteré mi lista original, lo cual no fue bueno. REVISIÓN: en lugar de 'reduce (iadd, LL)' o incluso 'reduce (iadd, (L para L en LL))', debe ajustar cada L devuelto en la lista(): 'reducir (iadd, (lista (L) para L en LL)) '. Esto obliga a cada L a copiarse. (Que es rápido, porque se conoce el tamaño). – ToolmakerSteve

2

Me gusta el método itertools.chain porque se ejecuta en tiempo lineal (la suma (...) se ejecuta en tiempo qurdratico) pero @Jochen no mostró cómo tratar las listas de longitud dinámica. Aquí hay una solución para la pregunta de OP.

import itertools 
list(itertools.chain(*[getNeighbors(n) for n in getNeighbors(vertex)])) 

Usted puede deshacerse de list(...) llamada si iterables es suficiente para ti.

+0

También puede deshacerse del desempaquetado '* [getNeighbors ...]' usando 'chain.from_iterable' como este:' list (itertools.chain.from_iterable (getNeighbors (n) for n in getNeighbors (vertex))) ' – emu

3

Ordenado por velocidad:

list_of_lists = [[x,1] for x in xrange(1000)] 

%timeit list(itertools.chain(*list_of_lists)) 
100000 loops, best of 3: 14.6 µs per loop 

%timeit list(itertools.chain.from_iterable(list_of_lists)) 
10000 loops, best of 3: 60.2 µs per loop 

min(timeit.repeat("ll=[];\nfor l in list_of_lists:\n ll.extend(l)", "list_of_lists=[[x,1] for x in xrange(1000)]",repeat=3, number=100))/100.0 
9.620904922485351e-05 

%timeit [y for z in list_of_lists for y in z] 
10000 loops, best of 3: 108 µs per loop 

%timeit sum(list_of_lists, []) 
100 loops, best of 3: 3.7 ms per loop 
+0

' itertools.chain (list_of_lists) 'está mal (no concatenará nada porque solo tiene un parámetro). Necesitas un '*' allí, o 'chain.from_iterable'. – interjay

0

usando .extend() (actualización en su lugar) en combinación con reducir en lugar de suma() (nuevo objeto cada vez) debe ser más eficiente sin embargo yo' soy demasiado perezoso para probar eso :)

mylist = [[1,2], [3,4], [5,6]] 
reduce(lambda acc_l, sl: acc_l.extend(sl) or acc_l, mylist) 
+0

De hecho es más rápido, pero como muestra [la respuesta de Yariv] (https://stackoverflow.com/a/33277438/160206), no es el enfoque más rápido. –