Pergunta

Gostaria de saber se existe um atalho para fazer uma lista simples de lista de listas em Python.

Eu posso fazer isso em um loop for, mas talvez haja alguma cool "one-liner"? Eu tentei com reduzir , 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'
Foi útil?

Solução

Dada uma lista de listas l,

flat_list = [item for sublist in l for item in sublist]

O que significa:

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

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

Aqui é a função correspondente:

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

Como prova, você pode usar 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 com base em + (incluindo a utilização implícita no sum) são, necessariamente, O(L**2) quando há L sublists - como a lista de resultado intermediário ficando cada vez mais, a cada passo um novo objeto lista resultado intermediário recebe alocado, e todos os itens no resultado intermediário anterior deve ser copiado (bem como alguns novos adicionados no final). Então, por simplicidade e sem perda real de generalidade, dizer que você tem L sublists de I itens cada: os primeiros itens I são copiados e para trás L-1 vezes, os itens de segunda I L-2 vezes, e assim por diante; número total de cópias é I vezes a soma de x para x a partir de um a L excluídos, isto é, I * (L**2)/2.

A lista de compreensão apenas gera uma lista, uma vez, e cópias cada item sobre (a partir de seu local original de residência para a lista de resultados) também exatamente uma vez.

Outras dicas

Você pode usar itertools.chain() :

>>> import itertools
>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]]
>>> merged = list(itertools.chain(*list2d))

ou, em Python> = 2,6, uso itertools.chain.from_iterable() , que não requer a descompactação da lista:

>>> import itertools
>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]]
>>> merged = list(itertools.chain.from_iterable(list2d))

Esta abordagem é sem dúvida mais legível do que [item for sublist in l for item in sublist] e parece ser mais rápido também:

[me@home]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99;import itertools' 'list(itertools.chain.from_iterable(l))'
10000 loops, best of 3: 24.2 usec per loop
[me@home]$ 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: 45.2 usec per loop
[me@home]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])'
1000 loops, best of 3: 488 usec per loop
[me@home]$ 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: 522 usec per loop
[me@home]$ python --version
Python 2.7.3

Nota do autor : Este é ineficiente. Mas divertido, porque monoids são impressionantes. Não é apropriado para a produção de código Python.

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

Isso só resume os elementos da iterable passado no primeiro argumento, tratando segundo argumento como o valor inicial da soma (se não for dada, 0 é usado no lugar e este caso lhe dará um erro).

Porque você está soma listas aninhadas, você realmente obter [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 uma outra solução.

I testados mais soluções com sugerida perfplot (um projeto de estimação da mina, essencialmente um invólucro em torno timeit) , e encontrou

functools.reduce(operator.iconcat, a, [])

para ser a solução mais rápida. (operator.iadd é igualmente rápido.)

 enter descrição da imagem aqui


Código para reproduzir o enredo:

import functools
import itertools
import numpy
import operator
import perfplot


def forfor(a):
    return [item for sublist in a for item in sublist]


def sum_brackets(a):
    return sum(a, [])


def functools_reduce(a):
    return functools.reduce(operator.concat, a)


def functools_reduce_iconcat(a):
    return functools.reduce(operator.iconcat, a, [])


def itertools_chain(a):
    return list(itertools.chain.from_iterable(a))


def numpy_flat(a):
    return list(numpy.array(a).flat)


def numpy_concatenate(a):
    return list(numpy.concatenate(a))


perfplot.show(
    setup=lambda n: [list(range(10))] * n,
    kernels=[
        forfor, sum_brackets, functools_reduce, functools_reduce_iconcat,
        itertools_chain, numpy_flat, numpy_concatenate
        ],
    n_range=[2**k for k in range(16)],
    logx=True,
    logy=True,
    xlabel='num lists'
    )
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 vez de retornar um valor útil (que espera reduce()).

A mais rápida maneira de fazer a versão reduce 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]

Aqui está uma abordagem geral que se aplica a números , cordas , aninhada listas e mistos recipientes.

Código

#from typing import Iterable 
from collections import Iterable                            # < py38


def flatten(items):
    """Yield items from any nested iterable; see Reference."""
    for x in items:
        if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
            for sub_x in flatten(x):
                yield sub_x
        else:
            yield x

Notas :

  • Em Python 3, yield from flatten(x) pode substituir for sub_x in flatten(x): yield sub_x
  • Em Python 3.8, classes base abstratas mudou de collection.abc ao módulo typing.

Demonstração

lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(flatten(lst))                                         # nested lists
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

mixed = [[1, [2]], (3, 4, {5, 6}, 7), 8, "9"]              # numbers, strs, nested & mixed
list(flatten(mixed))
# [1, 2, 3, 4, 5, 6, 7, 8, '9']

Referência

  • Esta solução é modificada a partir de uma receita em Beazley, D. e B. Jones. Receita 4.14, Python Cookbook 3ª Ed, O'Reilly Media Inc. Sebastopol, CA:.. 2013
  • SO anteriormente post, possivelmente, a demonstração originais.

Se você quer achatar a-estrutura de dados onde você não sabe quão profunda ela está aninhado você poderia usar iteration_utilities.deepflatten 1

>>> from iteration_utilities import deepflatten

>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> list(deepflatten(l, depth=1))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

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

É um gerador de modo que você precisa para converter o resultado para um list ou explicitamente iterate sobre ele.


Para nivelar apenas um nível e se cada um dos itens é a própria iterable você também pode usar iteration_utilities.flatten que em si é apenas um wrapper fino ao redor itertools.chain.from_iterable :

>>> from iteration_utilities import flatten
>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> list(flatten(l))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Apenas para adicionar alguns horários (baseado em Nico Schlömer resposta que não incluem a função apresentada nesta resposta):

 enter descrição da imagem aqui

É um gráfico log-log para acomodar a enorme gama de valores calibrados. Para raciocínio qualitativo: Menor é melhor

.

Os resultados mostram que se o iterable contém apenas alguns iterables internas então sum será mais rápido, no entanto por longos iterables apenas o itertools.chain.from_iterable, iteration_utilities.deepflatten ou a compreensão aninhada tem desempenho razoável com itertools.chain.from_iterable sendo o mais rápido (como já foi observado por Nico Schlömer ).

from itertools import chain
from functools import reduce
from collections import Iterable  # or from collections.abc import Iterable
import operator
from iteration_utilities import deepflatten

def nested_list_comprehension(lsts):
    return [item for sublist in lsts for item in sublist]

def itertools_chain_from_iterable(lsts):
    return list(chain.from_iterable(lsts))

def pythons_sum(lsts):
    return sum(lsts, [])

def reduce_add(lsts):
    return reduce(lambda x, y: x + y, lsts)

def pylangs_flatten(lsts):
    return list(flatten(lsts))

def flatten(items):
    """Yield items from any nested iterable; see REF."""
    for x in items:
        if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
            yield from flatten(x)
        else:
            yield x

def reduce_concat(lsts):
    return reduce(operator.concat, lsts)

def iteration_utilities_deepflatten(lsts):
    return list(deepflatten(lsts, depth=1))


from simple_benchmark import benchmark

b = benchmark(
    [nested_list_comprehension, itertools_chain_from_iterable, pythons_sum, reduce_add,
     pylangs_flatten, reduce_concat, iteration_utilities_deepflatten],
    arguments={2**i: [[0]*5]*(2**i) for i in range(1, 13)},
    argument_name='number of inner lists'
)

b.plot()

1 Disclaimer: Eu sou o autor dessa biblioteca

Eu levo minha declaração de volta. soma não é o vencedor. Embora seja mais rápido quando a lista é pequena. Mas os degrada significativamente o desempenho com listas maiores.

>>> timeit.Timer(
        '[item for sublist in l for item in sublist]',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10000'
    ).timeit(100)
2.0440959930419922

A versão soma ainda está sendo executado por mais de um minuto e não tem feito o processamento ainda!

Para listas médio:

>>> timeit.Timer(
        '[item for sublist in l for item in sublist]',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
    ).timeit()
20.126545906066895
>>> timeit.Timer(
        'reduce(lambda x,y: x+y,l)',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
    ).timeit()
22.242258071899414
>>> timeit.Timer(
        'sum(l, [])',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
    ).timeit()
16.449732065200806

Usando pequenas listas e timeit: number = 1000000

>>> timeit.Timer(
        '[item for sublist in l for item in sublist]',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
    ).timeit()
2.4598159790039062
>>> timeit.Timer(
        'reduce(lambda x,y: x+y,l)',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
    ).timeit()
1.5289170742034912
>>> timeit.Timer(
        'sum(l, [])',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
    ).timeit()
1.0598428249359131

Parece haver uma confusão com operator.add! Quando você adiciona duas listas juntos, o termo correto para isso é concat, não adicionar. operator.concat é o que você precisa para uso.

Se você está pensando funcional, é tão fácil como isso ::

>>> from functools import reduce
>>> list2d = ((1, 2, 3), (4, 5, 6), (7,), (8, 9))
>>> reduce(operator.concat, list2d)
(1, 2, 3, 4, 5, 6, 7, 8, 9)

Você vê reduzir aspectos, o tipo de sequência, então quando você fornecer uma tupla, você recebe de volta uma tupla. Vamos tentar com uma lista ::

>>> list2d = [[1, 2, 3],[4, 5, 6], [7], [8, 9]]
>>> reduce(operator.concat, list2d)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Aha, você recebe de volta uma lista.

Como sobre o desempenho ::

>>> list2d = [[1, 2, 3],[4, 5, 6], [7], [8, 9]]
>>> %timeit list(itertools.chain.from_iterable(list2d))
1000000 loops, best of 3: 1.36 µs per loop

from_iterable é bastante rápido! Mas há comparação para reduzir com concat.

>>> list2d = ((1, 2, 3),(4, 5, 6), (7,), (8, 9))
>>> %timeit reduce(operator.concat, list2d)
1000000 loops, best of 3: 492 ns per loop

Por que você usa estender?

reduce(lambda x, y: x+y, l)

Este deve funcionar bem.

Considere a instalação do pacote more_itertools .

> pip install more_itertools

Ele vem com uma implementação para flatten ( fonte , a partir do itertools receitas ):

import more_itertools


lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(more_itertools.flatten(lst))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

A partir da versão 2.4, você pode aplainar mais complicado, iterables aninhadas com more_itertools.collapse ( fonte , contribuiu por abarnet).

lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(more_itertools.collapse(lst)) 
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

lst = [[1, 2, 3], [[4, 5, 6]], [[[7]]], 8, 9]              # complex nesting
list(more_itertools.collapse(lst))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

A razão a sua função não funcionou: a estender estende gama no local e não devolvê-lo. Você ainda pode voltar x de lambda, usando algum truque:

reduce(lambda x,y: x.extend(y) or x, l)

Nota:. Estender é mais eficiente do que + em listas

Não reinventar a roda se você estiver usando Django :

>>> from django.contrib.admin.utils import flatten
>>> l = [[1,2,3], [4,5], [6]]
>>> flatten(l)
>>> [1, 2, 3, 4, 5, 6]

... Pandas :

>>> from pandas.core.common import flatten
>>> list(flatten(l))

... itertools :

>>> import itertools
>>> flatten = itertools.chain.from_iterable
>>> list(flatten(l))

... Matplotlib

>>> from matplotlib.cbook import flatten
>>> list(flatten(l))

... Unipath :

>>> from unipath.path import flatten
>>> list(flatten(l))

... Setuptools :

>>> from setuptools.namespaces import flatten
>>> list(flatten(l))
def flatten(l, a):
    for i in l:
        if isinstance(i, list):
            flatten(i, a)
        else:
            a.append(i)
    return a

print(flatten([[[1, [1,1, [3, [4,5,]]]], 2, 3], [4, 5],6], []))

# [1, 1, 1, 3, 4, 5, 2, 3, 4, 5, 6]

Uma característica ruim da função de Anil acima é que ele requer que o usuário sempre especificar manualmente o segundo argumento de ser uma lista [] vazio. Isto deve preferivelmente ser um padrão. Devido à forma como Python objetos trabalho, estes devem ser fixados dentro da função, não nos argumentos.

Aqui está uma função de trabalho:

def list_flatten(l, a=None):
    #check a
    if a is None:
        #initialize with empty list
        a = []

    for i in l:
        if isinstance(i, list):
            list_flatten(i, a)
        else:
            a.append(i)
    return a

Testing:

In [2]: lst = [1, 2, [3], [[4]],[5,[6]]]

In [3]: lst
Out[3]: [1, 2, [3], [[4]], [5, [6]]]

In [11]: list_flatten(lst)
Out[11]: [1, 2, 3, 4, 5, 6]

matplotlib.cbook.flatten() irá trabalhar para listas aninhadas, mesmo se eles ninho mais profundamente do que o exemplo.

import matplotlib
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
print(list(matplotlib.cbook.flatten(l)))
l2 = [[1, 2, 3], [4, 5, 6], [7], [8, [9, 10, [11, 12, [13]]]]]
print list(matplotlib.cbook.flatten(l2))

Resultado:

[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]

Esta é 18x mais rápido do que sublinhado ._ achatar:.

Average time over 1000 trials of matplotlib.cbook.flatten: 2.55e-05 sec
Average time over 1000 trials of underscore._.flatten: 4.63e-04 sec
(time for underscore._)/(time for matplotlib.cbook) = 18.1233394636

A resposta aceita não funcionou para mim quando se lida com listas baseadas em texto de comprimentos variáveis. Aqui é uma abordagem alternativa que fez o trabalho para mim.

l = ['aaa', 'bb', 'cccccc', ['xx', 'yyyyyyy']]

aceitado resposta que fez não trabalho:

flat_list = [item for sublist in l for item in sublist]
print(flat_list)
['a', 'a', 'a', 'b', 'b', 'c', 'c', 'c', 'c', 'c', 'c', 'xx', 'yyyyyyy']

New proposta solução que fez trabalho para mim:

flat_list = []
_ = [flat_list.extend(item) if isinstance(item, list) else flat_list.append(item) for item in l if item]
print(flat_list)
['aaa', 'bb', 'cccccc', 'xx', 'yyyyyyy']

versão recursiva

x = [1,2,[3,4],[5,[6,[7]]],8,9,[10]]

def flatten_list(k):
    result = list()
    for i in k:
        if isinstance(i,list):

            #The isinstance() function checks if the object (first argument) is an 
            #instance or subclass of classinfo class (second argument)

            result.extend(flatten_list(i)) #Recursive call
        else:
            result.append(i)
    return result

flatten_list(x)
#result = [1,2,3,4,5,6,7,8,9,10]

A seguir parece mais simples para mim:

>>> import numpy as np
>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> print (np.concatenate(l))
[1 2 3 4 5 6 7 8 9]

Pode-se também usar plana do NumPy :

import numpy as np
list(np.array(l).flat)

Editar 2016/11/02:. Só funciona quando sublists tem dimensões idênticas

Você pode usar numpy:
flat_list = list(np.concatenate(list_of_list))

código simples para underscore.py fã pacote

from underscore import _
_.flatten([[1, 2, 3], [4, 5, 6], [7], [8, 9]])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

Ele resolve todos os problemas achatar (item da lista nenhum ou nidificação complexo)

from underscore import _
# 1 is none list item
# [2, [3]] is complex nesting
_.flatten([1, [2, [3]], [4, 5, 6], [7], [8, 9]])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

Você pode instalar underscore.py com pip

pip install underscore.py
def flatten(alist):
    if alist == []:
        return []
    elif type(alist) is not list:
        return [alist]
    else:
        return flatten(alist[0]) + flatten(alist[1:])
flat_list = []
for i in list_of_list:
    flat_list+=i

Este código também funciona bem como ele só ampliar a lista todo o caminho. Embora seja muito semelhante, mas só tem um loop for. Por isso, têm menos complexidade do que adicionar 2 para loops.

Se você está disposto a desistir de uma pequena quantidade de velocidade para uma aparência mais limpa, então você poderia usar numpy.concatenate().tolist() ou numpy.concatenate().ravel().tolist():

import numpy

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]] * 99

%timeit numpy.concatenate(l).ravel().tolist()
1000 loops, best of 3: 313 µs per loop

%timeit numpy.concatenate(l).tolist()
1000 loops, best of 3: 312 µs per loop

%timeit [item for sublist in l for item in sublist]
1000 loops, best of 3: 31.5 µs per loop

Você pode descobrir mais aqui nos docs numpy.concatenate e numpy.ravel

solução mais rápida que eu encontrei (para uma lista grande de qualquer maneira):

import numpy as np
#turn list into an array and flatten()
np.array(l).flatten()

Feito! Pode, claro, transformá-lo de volta para uma lista de lista de execução (l)

Esta pode não ser a forma mais eficiente, mas eu pensei que colocar um one-liner (na verdade, um dois-liner). Ambas as versões irá funcionar nas listas hierarquia aninhada arbitrárias, e explora recursos de linguagem (Python3.5) e recursão.

def make_list_flat (l):
    flist = []
    flist.extend ([l]) if (type (l) is not list) else [flist.extend (make_list_flat (e)) for e in l]
    return flist

a = [[1, 2], [[[[3, 4, 5], 6]]], 7, [8, [9, [10, 11], 12, [13, 14, [15, [[16, 17], 18]]]]]]
flist = make_list_flat(a)
print (flist)

A saída é

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

Isso funciona em uma primeira maneira profundidade. A recursão vai para baixo até encontrar um elemento não-lista, em seguida, estende o flist variável local e, em seguida, reverte-lo para o pai. Sempre que flist é devolvido, ele é estendido para flist do pai na lista de compreensão. Portanto, na raiz, uma lista simples é retornado.

O de cima cria várias listas e devolve-os locais que são usados ??para estender a lista do pai. Acho que a volta para isso pode ser a criação de um flist gloabl, como abaixo.

a = [[1, 2], [[[[3, 4, 5], 6]]], 7, [8, [9, [10, 11], 12, [13, 14, [15, [[16, 17], 18]]]]]]
flist = []
def make_list_flat (l):
    flist.extend ([l]) if (type (l) is not list) else [make_list_flat (e) for e in l]

make_list_flat(a)
print (flist)

A saída é novamente

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

Embora eu não estou certo neste momento sobre a eficiência.

Nota : A seguir aplica-se a Python 3.3+ porque usa yield_from . six é também um pacote de terceiros, embora seja estável. Como alternativa, você poderia usar sys.version.


No caso de obj = [[1, 2,], [3, 4], [5, 6]], todas as soluções aqui são bons, incluindo lista de compreensão e itertools.chain.from_iterable.

No entanto, considerar este caso um pouco mais complexo:

>>> obj = [[1, 2, 3], [4, 5], 6, 'abc', [7], [8, [9, 10]]]

Há vários problemas aqui:

  • Um elemento, 6, é apenas um escalar; não é iterable, por isso as rotas acima falhará aqui.
  • Um elemento, 'abc', é tecnicamente iterable (todos strs são). No entanto, lendo nas entrelinhas um pouco, você não quer tratá-la como tal - você quer tratá-lo como um único elemento
  • .
  • O elemento final, [8, [9, 10]] é em si um iterable aninhada. compreensão lista básica e chain.from_iterable única extrair "1 nível baixo".

Você pode remediar esta situação da seguinte forma:

>>> from collections import Iterable
>>> from six import string_types

>>> def flatten(obj):
...     for i in obj:
...         if isinstance(i, Iterable) and not isinstance(i, string_types):
...             yield from flatten(i)
...         else:
...             yield i


>>> list(flatten(obj))
[1, 2, 3, 4, 5, 6, 'abc', 7, 8, 9, 10]

Aqui, você verificar que o sub-elemento (1) é iterable com Iterable , um ABC de itertools, mas também quer garantir que (2) o elemento é não "string-like."

Outra abordagem incomum que funciona para hetero e listas homogêneos de números inteiros:

from typing import List


def flatten(l: list) -> List[int]:
    """Flatten an arbitrary deep nested list of lists of integers.

    Examples:
        >>> flatten([1, 2, [1, [10]]])
        [1, 2, 1, 10]

    Args:
        l: Union[l, Union[int, List[int]]

    Returns:
        Flatted list of integer
    """
    return [int(i.strip('[ ]')) for i in str(l).split(',')]

Um método recursivo simples usando reduce de functools eo operador add em listas:

>>> from functools import reduce
>>> from operator import add
>>> flatten = lambda lst: [lst] if type(lst) is int else reduce(add, [flatten(ele) for ele in lst])
>>> flatten(l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

A função flatten leva em lst como parâmetro. Ele faz um loop todos os elementos de lst até inteiros atingindo (também pode alterar int para float, str, etc. para outros tipos de dados), que são adicionados ao valor de retorno da recursão mais externa.

A recursão, ao contrário de métodos como laços for e mônadas, é que é uma solução geral não limitados pela profundidade da lista . Por exemplo, uma lista com profundidade de 5 pode ser achatada da mesma forma que l:

>>> l2 = [[3, [1, 2], [[[6], 5], 4, 0], 7, [[8]], [9, 10]]]
>>> flatten(l2)
[3, 1, 2, 6, 5, 4, 0, 7, 8, 9, 10]
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top