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.
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)]
>>> 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.
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+)
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
.
Vous pouvez simplement faire le simple:
subs = []
for d in dirs:
subs.extend(os.listdir(d))
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 :-)
import itertools
x=[['b11','b12'],['b21','b22'],['b31']]
y=list(itertools.chain(*x))
print y
itertools fonctionnera à partir de python2.3 et plus
subs = []
map(subs.extend, (os.listdir(d) for d in dirs))
(mais la réponse de Ants est meilleure; +1 pour lui)
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()
.
Google m'a apporté la solution suivante:
def flatten(l):
if isinstance(l,list):
return sum(map(flatten,l))
else:
return l
Vous pouvez utiliser pyxtension :
from pyxtension.streams import stream
stream([ [1,2,3], [4,5], [], [6] ]).flatMap() == range(7)
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
If listA=[list1,list2,list3]
flattened_list=reduce(lambda x,y:x+y,listA)
Cela fera l'affaire.