2010-06-11 6 views
20

Supposons que j'ai une fonction comme ceci:Enchaînement de nombreuses listes en Python

def getNeighbors(vertex) 

qui retourne une liste de sommets qui sont voisins du sommet donné. Maintenant, je veux créer une liste avec tous les voisins des voisins. Je fais cela comme ceci:

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

Y a-t-il une façon plus pythonique de le faire?

Répondre

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

ou

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

+1 J'allais suggérer une compréhension de liste. À mon humble avis, c'est la façon la plus pythonienne. –

+1

Cependant, voir les comparaisons de temps, comme commentaires sous la réponse d'emu: à la fois "itertools.chain" et "reduce (iadd" sont plus de deux fois plus rapide que la compréhension de liste imbriquée - et BEAUCOUP plus rapide que sum(), qui se dégrade rapidement aveC# éléments traités – ToolmakerSteve

19

listes L'ajout peut être fait avec + et somme():

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

Merci - je * savais * qu'il devait y avoir un moyen de le faire avec somme!BTW, il n'était pas clair pour moi que cela fonctionnerait avec plus de 2 sous-listes, ou des listes de longueur variable; un exemple plus clair pourrait être: 'c = [[1, 2], [3, 4, 5], [6, 7]]' => '[1, 2, 3, 4, 5, 6, 7]' – ToolmakerSteve

+4

MAIS voir les horaires que j'ai fait comme commentaires sous la réponse d'emu. ** NE PAS UTILISER SUM - TRÈS LENT ** pour 100 listes de 100 articles! – ToolmakerSteve

+0

Echoue pour moi: '' l = [[3, 4, 5], [5, 6, 7], [6, 7, 8]] '' - ('' sum (l2, []) '' - > '' TypeError: un nombre entier est requis'' - cela pourrait-il être un problème selon la version de Python? J'utilise 2.7.6 – Zak

31

Comme d'habitude, le module itertools contient une solution:

>>> 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 vitesse compte, il peut être préférable d'utiliser ceci:

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

Le point de ce code est dans toute concaténer listes par list.extend où la compréhension de la liste ajouterait un élément par un, comme si vous appelez list.append. Cela économise un peu de frais généraux, rendant le premier (selon mes mesures) environ trois fois plus rapide. (L'opérateur iadd est normalement écrit += et fait la même chose que list.extend.)

compréhensions (la première solution par Ignacio) est encore le plus souvent dans le bon sens, il est plus facile à lire.

Mais évitez absolument d'utiliser sum(..., []), car il s'exécute en temps quadratique. C'est très peu pratique pour beaucoup de listes (plus d'une centaine).

+0

Merci pour le commentaire concernant la performance de la somme - J'ai aimé la compacité de ce code, tellement bon de savoir ne pas l'utiliser à grande échelle. À mon humble avis, la solution itertools'chain de Jochen à partir de '10 est une solution plus appropriée que de réduire: plus directement/simplement fait ce qui est demandé. – ToolmakerSteve

+0

Je reprends ce que j'ai dit à propos de la chaîne étant plus appropriée. Bien que je trouve plus facile à lire/comprendre, il y a un léger problème de performance, si le but ultime est de faire une liste: il repasse les éléments un à la fois, donc la liste ne peut pas prédire combien de temps cela va durer. En ce sens, il est plus similaire à la compréhension/append de liste qu'à réduire/étendre. Avec timeit, pour 100 listes x 100 éléments chacune, réduire/iadd vs chaîne à peu près le même temps (1.1). Avec 4x les données (200 listes x 200 éléments chacune), réduisez les gains (2.6 vs 4.0). – ToolmakerSteve

+1

AVERTISSEMENT: iadd MODIFIE la première liste transmise. Cela n'a pas d'importance dans l'exemple, car les listes sont les résultats d'une fonction. Mais j'ai fait un test où j'ai passé dans une liste de listes que j'avais pré-calculées. Modification de ma liste d'origine, ce qui n'était pas bon à faire. CORRECTIF: au lieu de 'réduire (iadd, LL)' ou même 'réduire (iadd, (L pour L dans LL))', doit renvoyer chaque L retourné dans list(): 'reduce (iadd, (list (L) pour L dans LL)) '. Cela force chaque L à être copié. (Ce qui est rapide, parce que la taille est connue.). – ToolmakerSteve

2

J'aime l'approche itertools.chain car elle s'exécute en temps linéaire (somme (...) s'exécute en temps quadratique) mais @Jochen n'a pas montré comment traiter les listes de longueur dynamique. Voici une solution pour la question de l'OP.

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

Vous pouvez vous débarrasser de list(...) appel si itérables est suffisant pour vous.

+0

Vous pouvez également vous débarrasser du déballage '* [getNeighbors ...]' en utilisant 'chain.from_iterable' comme ceci:' list (itertools.chain.from_iterable (getNeighbors (n) pour n dans getNeighbors (vertex))) ' – emu

3

Trié par vitesse:

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 erroné (il ne concaténera rien parce qu'il n'a qu'un seul paramètre). Vous avez besoin d'un '*' ou d'un 'chain.from_iterable'. – interjay

0

à l'aide .extend() (mise à jour en place) combinée à réduire au lieu de la somme () (nouvel objet à chaque fois) devrait cependant être plus efficace je m trop paresseux pour tester cela :)

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

Il est en effet plus rapide, mais comme le montre [la réponse de Yariv] (https://stackoverflow.com/a/33277438/160206), ce n'est pas l'approche la plus rapide. –