web-dev-qa-db-fra.com

compréhensions de liste python; compresser une liste de listes?

les mecs. J'essaie de trouver la solution la plus élégante à un problème et je me demandais si Python intègre quelque chose à ce que j'essaie de faire. 

Ce que je fais est ceci. J'ai une liste, A, et j'ai une fonction f qui prend un élément et retourne une liste. Je peux utiliser une compréhension de liste pour tout convertir en A comme tel;

[f(a) for a in A]

Mais cela retourne une liste de listes;

[a1,a2,a3] => [[b11,b12],[b21,b22],[b31,b32]]

Ce que je veux vraiment, c'est avoir la liste aplatie.

[b11,b12,b21,b22,b31,b32]

Maintenant, d'autres langues l'ont. il est traditionnellement appelé flatmap dans les langages de programmation fonctionnels, et .Net l'appelle SelectMany. Est-ce que python a quelque chose de similaire? Existe-t-il un moyen judicieux de mapper une fonction sur une liste et d’aplatir le résultat?

Le problème que j'essaie de résoudre est le suivant. en commençant par une liste de répertoires, trouvez tous les sous-répertoires. alors;

import os
dirs = ["c:\\usr", "c:\\temp"]
subs = [os.listdir(d) for d in dirs]
print subs

me donne actuellement une liste de listes, mais je veux vraiment une liste.

57
Steve Cooper

Vous pouvez avoir des itérations imbriquées dans une compréhension de liste unique:

[filename for path in dirs for filename in os.listdir(path)]
85
Ants Aasma
>>> listOfLists = [[1, 2],[3, 4, 5], [6]]
>>> reduce(list.__add__, listOfLists)
[1, 2, 3, 4, 5, 6]

J'imagine que la solution itertools est plus efficace que cela, mais elle est très pythonique et évite d'importer une bibliothèque uniquement pour une opération de liste unique.

60
Julian

Vous pouvez trouver une bonne réponse dans les recettes d'itertools:

def flatten(listOfLists):
    return list(chain.from_iterable(listOfLists))

(Remarque: nécessite Python 2.6+)

45
rob

La question proposée flatmap. Certaines implémentations sont proposées, mais elles peuvent créer inutilement des listes intermédiaires. Voici une implémentation basée sur des itérateurs.

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']

Dans Python 2.x, utilisez itertools.map à la place de map.

20
Wai Yip Tung

Vous pouvez simplement faire le simple:

subs = []
for d in dirs:
    subs.extend(os.listdir(d))
15
Anon

Vous pouvez concaténer des listes à l'aide de l'opérateur d'addition normal:

>>> [1, 2] + [3, 4]
[1, 2, 3, 4]

La fonction intégrée sum ajoutera les nombres dans une séquence et pourra éventuellement commencer à partir d'une valeur spécifique:

>>> sum(xrange(10), 100)
145

Combinez ce qui précède pour aplatir une liste de listes:

>>> sum([[1, 2], [3, 4]], [])
[1, 2, 3, 4]

Vous pouvez maintenant définir votre flatmap:

>>> def flatmap(f, seq):
...   return sum([f(s) for s in seq], [])
... 
>>> flatmap(range, [1,2,3])
[0, 0, 1, 0, 1, 2]

Edit: Je viens de voir la critique dans les commentaires de une autre réponse et je suppose qu'il est correct que Python construise et ramasse inutilement de nombreuses listes plus petites avec cette solution. Donc, la meilleure chose à dire, c'est que c'est très simple et concis si vous êtes habitué à la programmation fonctionnelle :-)

10
Martin Geisler
import itertools
x=[['b11','b12'],['b21','b22'],['b31']]
y=list(itertools.chain(*x))
print y

itertools fonctionnera à partir de python2.3 et plus

10
Sivachandran
subs = []
map(subs.extend, (os.listdir(d) for d in dirs))

(mais la réponse de Ants est meilleure; +1 pour lui)

7
RichieHindle

Vous pouvez essayer itertools.chain(), comme ceci:

import itertools
import os
dirs = ["c:\\usr", "c:\\temp"]
subs = list(itertools.chain(*[os.listdir(d) for d in dirs]))
print subs

itertools.chain() renvoie un itérateur, d'où le passage à list().

3
Steef

Google m'a apporté la solution suivante:

def flatten(l):
   if isinstance(l,list):
      return sum(map(flatten,l))
   else:
      return l
3
Vestel

Vous pouvez utiliser pyxtension :

from pyxtension.streams import stream
stream([ [1,2,3], [4,5], [], [6] ]).flatMap() == range(7)
2
asu
def flat_list(arr):
    send_back = []
    for i in arr:
        if type(i) == list:
            send_back += flat_list(i)
        else:
            send_back.append(i)
    return send_back
1
Vinod Kumar
If listA=[list1,list2,list3]
flattened_list=reduce(lambda x,y:x+y,listA)

Cela fera l'affaire.

0
Prabhudatta Das