Como fazer uma lista plana a partir da lista de listas?

Será que existe um atalho para fazer uma simples lista fora da lista em Python?

Eu posso fazer isso em um "para" loop, mas talvez haja algum cool "one-liner"? Eu tentei com reduce(), mas eu recebo um erro.

**Código***

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
reduce(lambda x, y: x.extend(y), l)

**Mensagem de erro***

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
AttributeError: 'NoneType' object has no attribute 'extend'
Solução

Dada uma lista de listas `l',

flat_list = [item para sub-lista em l para item em sub-lista]

o que significa:

flat_list = []
for sublist in l:
    for item in sublist:
        flat_list.append(item)

é mais rápido do que os atalhos postados até agora. (l é a lista para aplanar).

Aqui está a função correspondente:

flatten = lambda l: [item for sublist in l for item in sublist]

Como prova, você pode utilizar o módulo timeit na biblioteca padrão:

$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' '[item for sublist in l for item in sublist]'
10000 loops, best of 3: 143 usec per loop
$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])'
1000 loops, best of 3: 969 usec per loop
$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'reduce(lambda x,y: x+y,l)'
1000 loops, best of 3: 1.1 msec per loop

Explicação: os atalhos baseados em + (incluindo a utilização implícita em sum) são, necessariamente, O(L**2) quando existem sub-listas L -- como a lista de resultados intermediários continua a ficar mais longa, a cada passo um novo objeto da lista de resultados intermediários é alocado, e todos os itens no resultado intermediário anterior devem ser copiados (assim como alguns novos itens adicionados no final). Então, para simplificar e sem perda real de generalidade, digamos que você tenha L sublistas de I itens cada uma: o primeiro I itens são copiados L-1 vezes, o segundo I itens L-2 vezes, e assim por diante; o número total de cópias é I vezes a soma de x para x de 1 a L excluída, ou seja, I * (L**2)/2.

A compreensão da lista gera apenas uma lista, uma vez, e copia cada item (do seu local de residência original para a lista de resultados) também exatamente uma vez.

Comentários (29)

Nota do autor: Isto é ineficiente. Mas divertido, porque monoides são incríveis. It's não é apropriado para a produção de código Python.

>>> sum(l, [])
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Isto apenas soma os elementos do iterável passado no primeiro argumento, tratando o segundo argumento como o valor inicial da soma (se não for dado, 0 é utilizado em seu lugar e este caso lhe dará um erro).

Como você está somando listas aninhadas, você realmente recebe [1,3]+[2,4] como resultado de sum([[1,3],[2,4]],[]), que é igual a [1,3,2,4].

Note que só funciona em listas de listas. Para listas de listas de listas, você'vai precisar de outra solução.

Comentários (15)
from functools import reduce #python 3

>>> l = [[1,2,3],[4,5,6], [7], [8,9]]
>>> reduce(lambda x,y: x+y,l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

O método extend() no seu exemplo modifica x em vez de retornar um valor útil (que reduce() espera).

Uma maneira mais rápida de fazer a versão "reduzir" seria

>>> import operator
>>> l = [[1,2,3],[4,5,6], [7], [8,9]]
>>> reduce(operator.concat, l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Comentários (6)