Sim, eu sei que esse assunto já foi abordado antes ( aqui , aqui , aqui , aqui ), mas, tanto quanto eu sei, todas as soluções, exceto uma, falham em uma lista como esta:
L = [[[1, 2, 3], [4, 5]], 6]
Onde a saída desejada é
[1, 2, 3, 4, 5, 6]
Ou talvez ainda melhor, um iterador. A única solução que vi que funciona para um aninhamento arbitrário é encontrada nesta pergunta :
def flatten(x):
result = []
for el in x:
if hasattr(el, "__iter__") and not isinstance(el, basestring):
result.extend(flatten(el))
else:
result.append(el)
return result
flatten(L)
Este é o melhor modelo? Eu esqueci alguma coisa? Quaisquer problemas?
python
list
optimization
nested-lists
flatten
telliott99
fonte
fonte
list
pretende ser homogênea) não significa que é uma falha do Python e precisamos de um built-in para essa tarefaRespostas:
O uso das funções do gerador pode facilitar a leitura do seu exemplo e provavelmente aumentar o desempenho.
Python 2
Eu usei o ABC Iterável adicionado no 2.6.
Python 3
No Python 3,
basestring
não existe mais, mas você pode usar uma tupla destr
ebytes
obter o mesmo efeito lá.O
yield from
operador retorna um item de um gerador, um de cada vez. Esta sintaxe para delegar a um subgerador foi adicionada no 3.3fonte
l = ([[chr(i),chr(i-32)] for i in xrange(ord('a'), ord('z')+1)] + range(0,9))
em um piscar de olhos quando eu fiz issolist(flatten(l))
. Todos os outros começariam a trabalhar e demorariam para sempre!collections.Sequence
vez decollections.Iteratable
?for i in flatten(42): print (i)
. Isso pode ser corrigido movendo aisinstance
cláusula -test e else para fora dofor el
loop. (Então você pode jogar qualquer coisa para ele, e faria uma lista achatado fora dele)collections.Iterable
está obsoleto. Use emcollections.abc.Iterable
vez disso.Minha solução:
Um pouco mais conciso, mas praticamente o mesmo.
fonte
try: iter(x)
para testar se é iterável ... Mas não acho que ter que importar um módulo stdlib seja uma desvantagem que vale a pena evitar.int
def flatten(x): return [a for i in x for a in flatten(i)] if isinstance(x, collections.Iterable) else [x]
mas a legibilidade pode ser subjetiva aqui.if isinstance(x, collections.Iterable) and not isinstance(x, basestring)
Gerador usando recursão e digitação de patos (atualizado para Python 3):
fonte
for i in flatten(item): yield i
Versão do gerador da solução não recursiva do @ unutbu, conforme solicitado por @Andrew em um comentário:
Versão ligeiramente simplificada deste gerador:
fonte
Aqui está minha versão funcional do achatamento recursivo, que lida com tuplas e listas, e permite que você use qualquer combinação de argumentos posicionais. Retorna um gerador que produz toda a sequência em ordem, arg por arg:
Uso:
fonte
e
,a
,n
referem-seargs
paran
,intermediate
(ou o mais curtomid
ou você pode preferirelement
) paraa
eresult
parae
, então:flatten = lambda *args: (result for mid in args for result in (flatten(*mid) if isinstance(mid, (tuple, list)) else (mid,)))
compiler.ast.flatten
. Código ótimo e compacto, funciona para qualquer tipo de objeto (eu acho).Esta versão
flatten
evita o limite de recursão do python (e, portanto, funciona com iteráveis aninhados arbitrariamente profundos). É um gerador que pode lidar com strings e iterables arbitrários (mesmo os infinitos).Aqui estão alguns exemplos que demonstram seu uso:
Embora
flatten
possa manipular geradores infinitos, ele não pode manipular aninhamentos infinitos:fonte
sets
,dicts
,deques
,listiterators
,generators
, Filehandles e classes personalizadas com__iter__
definido são todas as instânciascollections.Iterable
, mas nãocollections.Sequence
. O resultado do achatamento de umdict
é um pouco duvidoso, mas, caso contrário, acho quecollections.Iterable
é um padrão melhor do quecollections.Sequence
. É definitivamente o mais liberal.collections.Iterable
é que isso inclui geradores infinitos. Alterei minha resposta para lidar com este caso.StopIteration
. Além disso, parece quewhile True: first = next(remainder)
pode ser substituído porfor first in remainder:
.try-except StopIteration block
.Aqui está outra resposta que é ainda mais interessante ...
Basicamente, ele converte a lista aninhada em uma seqüência de caracteres, usa um regex para remover a sintaxe aninhada e depois converte o resultado novamente em uma lista (nivelada).
fonte
[['C=64', 'APPLE ]['], ['Amiga', 'Mac', 'ST']]
:) Por outro lado, dada uma lista que se contém, ela se sairá um pouco melhor do que as outras respostas, gerando uma exceção em vez de apenas looping até que seja executado sem memória / recursão até esgotar a pilha ...[x for x in c]
é apenas uma maneira lenta e detalhada de fazer uma cópiac
, então por que você faria isso? Segundo, seu código claramente será convertido'APPLE ]['
em'APPLE '
, porque não lida com aspas, apenas assume que quaisquer colchetes são colchetes de lista.arr_str = str(arr)
e então[int(s) for s in re.findall(r'\d+', arr_str)]
realmente. Veja github.com/jorgeorpinel/flatten_nested_lists/blob/master/…fonte
Você pode usar
deepflatten
do pacote de terceirositeration_utilities
:É um iterador, portanto você precisa iterá-lo (por exemplo, envolvendo-o
list
ou usando-o em um loop). Internamente, ele usa uma abordagem iterativa em vez de uma abordagem recursiva e é escrito como extensão C, para que seja mais rápido que as abordagens python puras:Eu sou o autor da
iteration_utilities
biblioteca.fonte
Foi divertido tentar criar uma função que pudesse achatar uma lista irregular no Python, mas é claro que é para isso que serve o Python (para tornar a programação divertida). O gerador a seguir funciona bastante bem com algumas ressalvas:
Ele vai achatar tipos de dados que você talvez queira ficar sozinho (como
bytearray
,bytes
estr
objetos). Além disso, o código se baseia no fato de que solicitar um iterador de um não-iterável gera aTypeError
.Editar:
Não concordo com a implementação anterior. O problema é que você não deve conseguir achatar algo que não é iterável. É confuso e dá a impressão errada do argumento.
O gerador a seguir é quase o mesmo que o primeiro, mas não tem o problema de tentar achatar um objeto não iterável. Ele falha como seria de esperar quando um argumento inapropriado é dado a ele.
Testar o gerador funciona bem com a lista fornecida. No entanto, o novo código gerará um
TypeError
quando um objeto não iterável é fornecido a ele. Exemplos são mostrados abaixo do novo comportamento.fonte
Embora uma resposta elegante e muito pitônica tenha sido selecionada, eu apresentaria minha solução apenas para a revisão:
Por favor, diga o quão bom ou ruim é esse código?
fonte
isinstance(i, (tuple, list))
. Inicializar variáveis vazias é uma bandeira para eu olhar a estruturas alternativas de código, tipicamente compreensões, geradores, recursividade, etc.return type(l)(ret)
você também receberá o mesmo tipo de contêiner que foi passado. :)Eu prefiro respostas simples. Sem geradores. Sem limites de recursão ou recursão. Apenas iteração:
Isso funciona com duas listas: um loop for interno e um loop while externo.
O loop for interno percorre a lista. Se ele encontrar um elemento de lista, (1) usa list.extend () para nivelar a parte um nível de aninhamento e (2) alterna keepChecking para True. keepchecking é usado para controlar o loop while externo. Se o loop externo for definido como true, ele acionará o loop interno para outra passagem.
Esses passes continuam acontecendo até que nenhuma lista aninhada seja encontrada. Quando uma passagem finalmente ocorre onde nenhuma é encontrada, keepChecking nunca é acionado como true, o que significa que listIsNested permanece falso e o loop while externo é encerrado.
A lista nivelada é então retornada.
Execução de teste
[1, 2, 3, 4, 100, 200, 300, 1000, 2000, 3000]
fonte
Aqui está uma função simples que nivela listas de profundidade arbitrária. Sem recursão, para evitar o estouro da pilha.
fonte
Estou surpreso que ninguém tenha pensado nisso. Maldita recursão, não recebo as respostas recursivas que as pessoas avançadas aqui fizeram. de qualquer maneira aqui está a minha tentativa sobre isso. ressalva é que é muito específico para o caso de uso do OP
resultado:
fonte
Eu não passei por todas as respostas já disponíveis aqui, mas aqui está uma lista que eu inventei, tomando emprestado da maneira do lisp de processar o primeiro e o resto da lista
aqui está um caso simples e um não tão simples -
fonte
def foo():
é uma linha separada. Além disso, isso é muito ilegível.Ao tentar responder a essa pergunta, você realmente precisa fornecer as limitações do código que propõe como solução. Se fosse apenas sobre performances, eu não me importaria muito, mas a maioria dos códigos propostos como solução (incluindo a resposta aceita) falha ao achatar qualquer lista com profundidade maior que 1000.
Quando digo a maioria dos códigos quero dizer todos os códigos que usam qualquer forma de recursão (ou chamam uma função de biblioteca padrão que é recursiva). Todos esses códigos falham porque, para cada chamada recursiva feita, a pilha (chamada) cresce em uma unidade e a pilha de chamadas python (padrão) tem um tamanho de 1000.
Se você não estiver muito familiarizado com a pilha de chamadas, talvez o seguinte ajude (caso contrário, você pode simplesmente rolar para a Implementação ).
Tamanho da pilha de chamadas e programação recursiva (analogia do calabouço)
Encontrar o tesouro e sair
Imagine que você entra em uma enorme masmorra com salas numeradas , procurando um tesouro. Você não conhece o lugar, mas tem algumas indicações sobre como encontrar o tesouro. Cada indicação é um enigma (a dificuldade varia, mas você não pode prever o quão difícil será). Você decide pensar um pouco sobre uma estratégia para economizar tempo, faz duas observações:
Ao entrar na masmorra, você nota um pequeno caderno aqui. Você decide usá-lo para anotar todas as salas que sair depois de resolver um enigma (ao entrar em uma nova sala), dessa forma você poderá voltar para a entrada. Essa é uma ideia genial, você nem gasta um centavo implementando sua estratégia.
Você entra na masmorra, resolvendo com grande sucesso os primeiros 1001 enigmas, mas aí vem algo que você não havia planejado, não havia mais espaço no caderno emprestado. Você decide abandonar sua missão, pois prefere não ter o tesouro do que se perder para sempre dentro da masmorra (que parece realmente inteligente).
Executando um programa recursivo
Basicamente, é exatamente a mesma coisa que encontrar o tesouro. A masmorra é a memória do computador , seu objetivo agora não é encontrar um tesouro, mas calcular algumas funções (encontre f (x) para um dado x ). As indicações são simplesmente sub-rotinas que o ajudarão a resolver f (x) . Sua estratégia é a mesma da pilha de chamadas , o notebook é a pilha, as salas são os endereços de retorno das funções:
O problema que você encontrou no calabouço será o mesmo aqui, a pilha de chamadas tem um tamanho finito (aqui 1000) e, portanto, se você digitar muitas funções sem retornar, preencherá a pilha de chamadas e ocorrerá um erro que parecerá como
"Caro aventureiro, sinto muito, mas seu notebook está cheio":RecursionError: maximum recursion depth exceeded
. Observe que você não precisa de recursão para preencher a pilha de chamadas, mas é muito improvável que um programa não recursivo chame 1000 funções sem nunca retornar. É importante entender também que, quando você retorna de uma função, a pilha de chamadas é liberada do endereço usado (daí o nome "pilha", o endereço de retorno é inserido antes de inserir uma função e retirado ao retornar). No caso especial de uma recursão simples (uma funçãof
que se autodenomina repetidamente - você entraráf
repetidamente até que o cálculo esteja concluído (até que o tesouro seja encontrado) e retornará def
até voltar ao local para o qual você ligouf
. A pilha de chamadas nunca será liberada de nada até o final, onde será liberada de todos os endereços de retorno, um após o outro.Como evitar esse problema?
Na verdade, isso é bem simples: "não use recursão se você não souber o quão profundo ela pode ser". Isso nem sempre é verdade, pois em alguns casos, a recursão da chamada de cauda pode ser otimizada (TCO) . Mas em python, esse não é o caso, e mesmo a função recursiva "bem escrita" não otimiza o uso da pilha. Há um post interessante de Guido sobre esta questão: Eliminação da recursão da cauda .
Existe uma técnica que você pode usar para tornar iterativa qualquer função recursiva, essa técnica que poderíamos chamar de trazer seu próprio notebook . Por exemplo, em nosso caso específico, simplesmente estamos explorando uma lista, entrar em uma sala é equivalente a entrar em uma sub-lista; a pergunta que você deve fazer é: como posso voltar de uma lista para sua lista de pais? A resposta não é tão complexa, repita o seguinte até que
stack
esteja vazio:address
eindex
em umstack
ao entrar em uma nova sub-lista (note que um endereço de lista + índice é também um endereço, portanto, apenas usar exatamente a mesma técnica usada pela pilha de chamadas);yield
ele (ou adiciona-o a uma lista);stack
retornoaddress
(eindex
) .Observe também que isso é equivalente a um DFS em uma árvore em que alguns nós são sublistas
A = [1, 2]
e outros são itens simples:0, 1, 2, 3, 4
(paraL = [0, [1,2], 3, 4]
). A árvore fica assim:A pré-ordem de travessia do DFS é: L, 0, A, 1, 2, 3, 4. Lembre-se de que, para implementar um DFS iterativo, você também "precisa" de uma pilha. A implementação que propus antes resulta em ter os seguintes estados (para
stack
eflat_list
):Neste exemplo, o tamanho máximo da pilha é 2, porque a lista de entrada (e, portanto, a árvore) possui profundidade 2.
Implementação
Para a implementação, em python, você pode simplificar um pouco usando iteradores em vez de listas simples. As referências aos (sub) iteradores serão usadas para armazenar os endereços de retorno das sublistas (em vez de ter o endereço da lista e o índice). Essa não é uma grande diferença, mas acho que é mais legível (e também um pouco mais rápida):
Além disso, observe que em
is_list_like
I haveisinstance(item, list)
, que poderia ser alterado para lidar com mais tipos de entrada, aqui eu só queria ter a versão mais simples em que (iterável) seja apenas uma lista. Mas você também pode fazer isso:Isso considera as strings como "itens simples" e, portanto
flatten_iter([["test", "a"], "b])
, retornará["test", "a", "b"]
e não["t", "e", "s", "t", "a", "b"]
. Observe que, nesse caso,iter(item)
é chamado duas vezes em cada item, vamos fingir que é um exercício para o leitor tornar esse limpador mais limpo.Testes e observações sobre outras implementações
No final, lembre-se de que você não pode imprimir uma lista infinitamente aninhada
L
usando,print(L)
porque internamente ela usará chamadas recursivas para__repr__
(RecursionError: maximum recursion depth exceeded while getting the repr of an object
). Pelo mesmo motivo, as soluções deflatten
envolvimentostr
falharão com a mesma mensagem de erro.Se você precisar testar sua solução, poderá usar esta função para gerar uma lista aninhada simples:
O que fornece:
build_deep_list(5)
>>>[4, [3, [2, [1, [0]]]]]
.fonte
Aqui está a
compiler.ast.flatten
implementação na 2.7.5:Existem métodos melhores e mais rápidos (se você chegou aqui, já os viu)
Observe também:
fonte
totalmente hacky, mas acho que funcionaria (dependendo do seu data_type)
fonte
Basta usar uma
funcy
biblioteca:pip install funcy
fonte
Aqui está outra abordagem py2, não tenho certeza se é a mais rápida, a mais elegante nem a mais segura ...
Ele pode ignorar qualquer tipo específico (ou derivado) desejado, retorna um iterador, para que você possa convertê-lo em qualquer contêiner específico, como lista, tupla, ditado ou simplesmente consumi-lo para reduzir o consumo de memória, para melhor ou pior ele pode manipular objetos não-iteráveis iniciais, como int ...
Observe que a maior parte do trabalho pesado é feita em C, já que, até onde eu sei, é implementado o itertec, por isso, embora seja recursivo, o AFAIK não é limitado pela profundidade da recursão do python, pois as chamadas de função estão acontecendo em C, embora isso não significa que você esteja limitado pela memória, especialmente no OS X, onde o tamanho da pilha tem um limite rígido a partir de hoje (OS X Mavericks) ...
existe uma abordagem um pouco mais rápida, mas um método menos portátil, use-o somente se você puder assumir que os elementos base da entrada podem ser explicitamente determinados, caso contrário, você obterá uma recursão infinita e o OS X com seu tamanho limitado de pilha será lançar uma falha de segmentação rapidamente ...
aqui estamos usando conjuntos para verificar o tipo, para que O (1) vs O (número de tipos) verifique se um elemento deve ou não ser ignorado, embora, é claro, qualquer valor com o tipo derivado dos tipos ignorados declarados falhe , é por isso que está usando
str
,unicode
então use-o com cuidado ...testes:
fonte
Sem usar nenhuma biblioteca:
fonte
Usando
itertools.chain
:Ou sem encadeamento:
fonte
Eu usei recursivo para resolver lista aninhada com qualquer profundidade
Então, depois de definir a função combine_nlist, é fácil usar esta função do flatting. Ou você pode combiná-lo em uma função. Eu gosto da minha solução porque ela pode ser aplicada a qualquer lista aninhada.
resultado
fonte
current_value = combiner(current_value,each_item) RecursionError: maximum recursion depth exceeded
A maneira mais fácil é usar a biblioteca de metamorfose usando
pip install morph
.O código é:
fonte
Estou ciente de que já existem muitas respostas impressionantes, mas eu gostaria de adicionar uma resposta que use o método de programação funcional para resolver a questão. Nesta resposta, uso dupla recursão:
resultado:
fonte
Não tenho certeza se isso é necessariamente mais rápido ou mais eficaz, mas é isso que eu faço:
A
flatten
função aqui transforma a lista em uma cadeia de caracteres, retira todos os colchetes, anexa colchetes de volta às extremidades e a transforma novamente em uma lista.Embora, se você soubesse que teria colchetes na sua lista em cadeias de caracteres,
[[1, 2], "[3, 4] and [5]"]
teria que fazer outra coisa.fonte
Este é um implemento simples de achatar em python2
fonte
Isso achatará uma lista ou dicionário (ou lista de listas ou dicionários de dicionários etc.). Ele assume que os valores são cadeias e cria uma cadeia que concatena cada item com um argumento separador. Se você quiser, poderá usar o separador para dividir o resultado em um objeto de lista posteriormente. Ele usa recursão se o próximo valor for uma lista ou uma sequência. Use o argumento key para saber se você deseja as chaves ou os valores (defina key como false) do objeto de dicionário.
rendimentos:
fonte
Se você gosta de recursão, isso pode ser uma solução interessante para você:
Na verdade, adaptei isso a partir de algum código prático do esquema que havia escrito há algum tempo.
Aproveitar!
fonte
Eu sou novo em python e venho de um fundo lisp. Isto é o que eu vim com (confira os nomes de var para lulz):
Parece funcionar. Teste:
retorna:
fonte