Ответ 1
Вы можете иметь вложенные итерации в одном понимании списка:
[filename for path in dirs for filename in os.listdir(path)]
ребята. Я пытаюсь найти самое элегантное решение проблемы и задаюсь вопросом, есть ли у python что-то встроенное для того, что я пытаюсь сделать.
Что я делаю, так это. У меня есть список A
, и у меня есть функция f
, которая берет элемент и возвращает список. Я могу использовать понимание списка, чтобы преобразовать все в A
так:
[f(a) for a in A]
Но это возвращает список списков,
[a1,a2,a3] => [[b11,b12],[b21,b22],[b31,b32]]
Я действительно хочу получить сплющенный список,
[b11,b12,b21,b22,b31,b32]
Теперь, на других языках; он традиционно называется flatmap
в языках функционального программирования, а .Net называет его SelectMany
. У python есть что-то подобное? Есть ли опрятный способ сопоставить функцию над списком и сгладить результат?
Фактическая проблема, которую я пытаюсь решить, заключается в следующем; начиная со списка каталогов, найдите все подкаталоги. так;
import os
dirs = ["c:\\usr", "c:\\temp"]
subs = [os.listdir(d) for d in dirs]
print subs
currentliy дает мне список списков, но мне действительно нужен список.
Вы можете иметь вложенные итерации в одном понимании списка:
[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]
Я предполагаю, что решение itertools более эффективно, чем это, но это очень pythonic, и он не позволяет импортировать библиотеку только для одной операции с списком.
Вы можете найти хороший ответ в рецепты itertools:
def flatten(listOfLists):
return list(chain.from_iterable(listOfLists))
(Примечание: требуется Python 2.6 +)
Вы можете просто сделать прямо:
subs = []
for d in dirs:
subs.extend(os.listdir(d))
Предлагаемый вопрос flatmap
. Предложены некоторые варианты реализации, но они могут создавать ненужные промежуточные списки. Вот одна реализация, основанная на итераторах.
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']
В Python 2.x используйте itertools.map
вместо map
.
Вы можете объединить списки, используя обычный оператор сложения:
>>> [1, 2] + [3, 4]
[1, 2, 3, 4]
Встроенная функция sum
добавит числа в последовательность и может необязательно начать с определенного значения:
>>> sum(xrange(10), 100)
145
Объедините приведенное выше, чтобы сгладить список списков:
>>> sum([[1, 2], [3, 4]], [])
[1, 2, 3, 4]
Теперь вы можете определить свой flatmap
:
>>> def flatmap(f, seq):
... return sum([f(s) for s in seq], [])
...
>>> flatmap(range, [1,2,3])
[0, 0, 1, 0, 1, 2]
Изменить: Я только что видел критику в комментариях для другого ответа, и я думаю, это правильно, что Python будет бесполезно строить и мусор собирайте много меньших списков с помощью этого решения. Поэтому самое лучшее, что можно сказать об этом, это то, что он очень прост и краток, если вы привыкли к функциональному программированию: -)
import itertools
x=[['b11','b12'],['b21','b22'],['b31']]
y=list(itertools.chain(*x))
print y
itertools будет работать от python2.3 и выше
subs = []
map(subs.extend, (os.listdir(d) for d in dirs))
(но ответ Ants лучше, +1 для него)
Вы можете попробовать itertools.chain()
, например:
import itertools
import os
dirs = ["c:\\usr", "c:\\temp"]
subs = list(itertools.chain(*[os.listdir(d) for d in dirs]))
print subs
itertools.chain()
возвращает итератор, поэтому переход к list()
.
Google принес мне следующее решение:
def flatten(l):
if isinstance(l,list):
return sum(map(flatten,l))
else:
return l
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
Вы можете использовать pyxtension:
from pyxtension.streams import stream
stream([ [1,2,3], [4,5], [], [6] ]).flatMap() == range(7)
If listA=[list1,list2,list3]
flattened_list=reduce(lambda x,y:x+y,listA)
Это сделает.