понимание списка 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 54

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.

вы могли бы просто сделать простой:

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

вы можете объединять списки с помощью обычного оператора сложения:

>>> [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 и выше

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

(но ответ Муравьев лучше; +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)

Это будет делать.