понимание списка python; сжатие списка списков?
ребята. Я пытаюсь найти самое элегантное решение проблемы и задался вопросом, есть ли у 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 дает мне список-из-списков, но я действительно хочу список.
13 ответов:
вы можете иметь вложенные итерации в одном списке понимания:
[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 более эффективно, чем это, но это очень питоническое и позволяет избежать необходимости импортировать библиотеку только ради одной операции списка.
вы можете найти хороший ответ в рецепты itertools:
def flatten(listOfLists): return list(chain.from_iterable(listOfLists))
(Примечание: требуется Python 2.6+)
на вопрос, предложенный
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]
Edit: я только что видел критику в комментариях для еще один ответ и я думаю, что это исправьте, что Python будет напрасно строить и собирать мусор много меньших списков с этим решением. Поэтому самое лучшее, что можно сказать о нем, это то, что он очень прост и лаконичен, если вы привыкли к функциональному программированию :-)
import itertools x=[['b11','b12'],['b21','b22'],['b31']] y=list(itertools.chain(*x)) print y
itertools будет работать с python2. 3 и выше
вы могли бы попробовать
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)