Estou percorrendo uma lista de tuplas no Python e estou tentando removê-las se elas atenderem a certos critérios.
for tup in somelist:
if determine(tup):
code_to_remove_tup
O que devo usar no lugar de code_to_remove_tup
? Não consigo descobrir como remover o item dessa maneira.
Respostas:
Você pode usar uma compreensão de lista para criar uma nova lista contendo apenas os elementos que não deseja remover:
Ou, atribuindo à fatia
somelist[:]
, você pode alterar a lista existente para conter apenas os itens que deseja:Essa abordagem pode ser útil se houver outras referências a
somelist
essa necessidade para refletir as alterações.Em vez de uma compreensão, você também pode usar
itertools
. No Python 2:Ou no Python 3:
Por uma questão de clareza e para aqueles que consideram o uso da
[:]
notação hackish ou fuzzy, aqui está uma alternativa mais explícita. Teoricamente, ele deve executar o mesmo em relação ao espaço e ao tempo do que os versículos acima.Também funciona em outros idiomas que podem não ter a capacidade de substituir itens das listas Python, com modificações mínimas. Por exemplo, nem todos os idiomas convertem listas vazias para o mesmo
False
que o Python. Você pode substituirwhile somelist:
por algo mais explícito comowhile len(somelist) > 0:
.fonte
somelist[:] = (x for x in somelist if determine(x))
isso para criar um gerador que pode não criar cópias desnecessárias.list_ass_slice()
função que implementasomelist[:]=
chamadasPySequence_Fast()
internamente. Esta função sempre retorna uma lista ou seja, a solução da @Alex Martelli que já utiliza uma lista em vez de um gerador é provavelmente mais eficientesomelist
seria alterada nos dois métodos?As respostas que sugerem a compreensão da lista ESTÃO QUASE corretas - exceto que elas criam uma lista completamente nova e depois atribuem o mesmo nome à lista antiga, pois NÃO modificam a lista antiga no local. É diferente do que você faria com a remoção seletiva, como na sugestão de @ Lennart - é mais rápido, mas se sua lista for acessada por várias referências, o fato de você estar apenas substituindo uma das referências e NÃO alterando o objeto da lista por si só pode levar a erros sutis e desastrosos.
Felizmente, é extremamente fácil obter a velocidade da compreensão da lista E a semântica necessária da alteração no local - apenas código:
Observe a diferença sutil com outras respostas: esta NÃO está atribuindo a um nome de barra - está atribuindo a uma fatia da lista que por acaso é a lista inteira, substituindo assim o conteúdo da lista no mesmo objeto de lista Python , em vez de apenas recolocar uma referência (do objeto de lista anterior para o novo objeto de lista) como as outras respostas.
fonte
a
pelo conteúdo do dictb
, usea.clear(); a.update(b)
.x = ['foo','bar','baz']; y = x; x = [item for item in x if determine(item)];
Isso reatribuix
ao resultado da compreensão da lista, masy
ainda se refere à lista original['foo','bar','baz']
. Se você esperavax
ey
se referiu à mesma lista, pode ter introduzido bugs. Você evitar isso através da atribuição de uma fatia de toda a lista, como Alex shows, e eu mostro aqui:x = ["foo","bar","baz"]; y = x; x[:] = [item for item in x if determine(item)];
. A lista é modificada no local. garantindo que todas as referências à lista ( aquix
ey
aqui) se refiram à nova lista.filter
a função também cria uma nova lista, não modifica elementos no lugar ... únicaolist[:] = [i for i in olist if not dislike(i)]
Você precisa tirar uma cópia da lista e iterá-la primeiro, ou a iteração falhará com o que pode ser resultados inesperados.
Por exemplo (depende de que tipo de lista):
Um exemplo:
fonte
list(somelist)
irá converter um iterável em uma lista.somelist[:]
faz uma cópia de um objeto que suporta fatiamento. Então eles não fazem necessariamente a mesma coisa. Neste caso, eu quero fazer uma cópia dosomelist
objeto, então eu uso[:]
remove()
precisa passar por cima da lista INTEIRA para cada iteração, de modo que levará uma eternidade.Você precisa ir para trás, caso contrário, é como serrar o galho de árvore em que você está sentado :-)
Usuários do Python 2: substitua
range
porxrange
para evitar a criação de uma lista codificadafonte
reversed()
builtinenumerate
retorna um iterador ereversed
espera uma sequência. Eu acho que você poderia fazerreversed(list(enumerate(somelist)))
se não se importar em criar uma lista extra na memória.m
mais lento.Tutorial oficial do Python 2 4.2. "para declarações"
https://docs.python.org/2/tutorial/controlflow.html#for-statements
Esta parte dos documentos deixa claro que:
[:]
Documentação do Python 2 7.3. "A declaração for"
https://docs.python.org/2/reference/compound_stmts.html#for
Esta parte dos documentos diz mais uma vez que você precisa fazer uma cópia e fornece um exemplo real de remoção:
No entanto, eu discordo dessa implementação, pois ele
.remove()
precisa repetir a lista inteira para encontrar o valor.Melhores soluções alternativas
Ou:
inicie uma nova matriz do zero e
.append()
volte ao final: https://stackoverflow.com/a/1207460/895245Este tempo é eficiente, mas com menos espaço, porque mantém uma cópia da matriz durante a iteração.
use
del
com um índice: https://stackoverflow.com/a/1207485/895245Isso economiza mais espaço, pois dispensa a cópia da matriz, mas é menos eficiente porque as listas de CPython são implementadas com matrizes dinâmicas .
Isso significa que a remoção do item requer a troca de todos os itens seguintes de volta por um, que é O (N).
Geralmente você só quer ir mais rápido
.append()
opção por padrão, a menos que a memória seja uma grande preocupação.Python poderia fazer isso melhor?
Parece que essa API específica do Python poderia ser melhorada. Compare, por exemplo, com:
std::vector::erase
que retorna um interator válido para o elemento após o removidoambos deixam claro que você não pode modificar uma lista que está sendo iterada, exceto com o próprio iterador, e oferece maneiras eficientes de fazer isso sem copiar a lista.
Talvez a lógica subjacente é que as listas Python são assumidos como matriz dinâmica apoiada, e, por conseguinte, qualquer tipo de remoção será tempo de qualquer maneira ineficiente, enquanto Java tem uma hierarquia de interface mais agradável com ambos
ArrayList
eLinkedList
implementaçõesListIterator
.Parece não haver um tipo explícito de lista vinculada no stdlib do Python: Lista vinculada do Python
fonte
Sua melhor abordagem para esse exemplo seria uma compreensão de lista
Nos casos em que você está fazendo algo mais complexo do que chamar uma
determine
função, prefiro construir uma nova lista e simplesmente anexá-la à medida que for avançando. Por exemploCopiar a lista usando
remove
pode fazer com que seu código pareça um pouco mais limpo, conforme descrito em uma das respostas abaixo. Definitivamente, você não deve fazer isso para listas extremamente grandes, pois isso envolve primeiro copiar a lista inteira e também executar umaO(n)
remove
operação para cada elemento que está sendo removido, tornando este umO(n^2)
algoritmo.fonte
Para quem gosta de programação funcional:
ou
fonte
filter
, e mais pitônicos. 2. Se você precisarlambda
usarmap
oufilter
, a lista comp ou genexpr é sempre a melhor opção;map
efilter
pode ser um pouco mais rápido quando a função de transformação / predicado é um Python embutido implementado em C e o iterável não é trivialmente pequeno, mas eles são sempre mais lentos quando você precisa de umlambda
que o listcomp / genexpr possa evitar.Eu precisava fazer isso com uma lista enorme e duplicar a lista parecia caro, especialmente porque, no meu caso, o número de exclusões seria pequeno se comparado aos itens que permanecem. Adotei essa abordagem de baixo nível.
O que não sei é a eficiência de algumas exclusões em comparação com a cópia de uma lista grande. Por favor, comente se você tiver alguma idéia.
fonte
list
uma estrutura de dados em primeiro lugar deve ser cuidadosamente considerada, pois a remoção do meio de uma lista leva um tempo linear no comprimento da lista. Se você realmente não precisa de acesso aleatório ao k-ésimo item seqüencial, talvez considereOrderedDict
?newlist = []
, enewlist.append(array[i])
logo antesdel array[i]
?list()
for uma lista vinculada, o acesso aleatório é caro, selist()
for uma matriz, as exclusões são caras, porque precisam mover todos os seguintes elementos adiante. Um iterador decente pode melhorar as coisas para a implementação da lista vinculada. No entanto, isso pode ser eficiente em termos de espaço.Pode ser inteligente também criar apenas uma nova lista se o item atual da lista atender aos critérios desejados.
tão:
e para evitar ter que codificar novamente o projeto inteiro com o novo nome da lista:
observe, da documentação do Python:
fonte
Esta resposta foi originalmente escrita em resposta a uma pergunta que foi marcada como duplicada: Removendo coordenadas da lista em python
Existem dois problemas no seu código:
1) Ao usar remove (), você tenta remover números inteiros, ao passo que precisa remover uma tupla.
2) O loop for ignorará os itens da sua lista.
Vamos analisar o que acontece quando executamos seu código:
O primeiro problema é que você está passando 'a' e 'b' para remove (), mas remove () aceita apenas um único argumento. Então, como podemos fazer com que remove () funcione corretamente com sua lista? Precisamos descobrir o que é cada elemento da sua lista. Nesse caso, cada um é uma tupla. Para ver isso, vamos acessar um elemento da lista (a indexação começa em 0):
Aha! Cada elemento de L1 é na verdade uma tupla. Então é isso que precisamos passar para remover (). Tuplas em python são muito fáceis, elas são simplesmente feitas colocando valores entre parênteses. "a, b" não é uma tupla, mas "(a, b)" é uma tupla. Então, modificamos seu código e o executamos novamente:
Esse código é executado sem nenhum erro, mas vamos olhar para a lista que ele gera:
Por que (1, -2) ainda está na sua lista? Acontece que modificar a lista enquanto usa um loop para iterar sobre ela é uma péssima idéia sem cuidados especiais. O motivo pelo qual (1, -2) permanece na lista é que os locais de cada item na lista foram alterados entre as iterações do loop for. Vamos ver o que acontece se alimentarmos o código acima com uma lista mais longa:
Como você pode deduzir desse resultado, sempre que a instrução condicional for avaliada como verdadeira e um item da lista for removido, a próxima iteração do loop ignorará a avaliação do próximo item da lista porque seus valores agora estão localizados em índices diferentes.
A solução mais intuitiva é copiar a lista, iterar sobre a lista original e modificar apenas a cópia. Você pode tentar fazer o seguinte:
No entanto, a saída será idêntica à anterior:
Isso ocorre porque, quando criamos o L2, o python não criou um novo objeto. Em vez disso, apenas referenciou L2 ao mesmo objeto que L1. Podemos verificar isso com 'is', que é diferente de meramente "igual" (==).
Podemos fazer uma cópia verdadeira usando copy.copy (). Então tudo funciona como esperado:
Finalmente, há uma solução mais limpa do que ter que fazer uma cópia totalmente nova de L1. A função reversed ():
Infelizmente, não posso descrever adequadamente como reversed () funciona. Retorna um objeto 'listreverseiterator' quando uma lista é passada para ele. Para fins práticos, você pode pensar nisso como criar uma cópia invertida de seu argumento. Esta é a solução que eu recomendo.
fonte
Se você quiser fazer mais alguma coisa durante a iteração, pode ser bom obter o índice (que garante que você possa fazer referência a ele, por exemplo, se você tiver uma lista de dictos) e o conteúdo real do item da lista.
enumerate
dá acesso ao item e ao índice de uma só vez.reversed
é para que os índices que você deseja excluir posteriormente não sejam alterados.fonte
Você pode querer usar
filter()
disponíveis como internos.Para mais detalhes confira aqui
fonte
A maioria das respostas aqui deseja que você crie uma cópia da lista. Eu tinha um caso de uso em que a lista era bastante longa (itens de 110 mil) e era mais inteligente continuar reduzindo a lista.
Primeiro, você precisará substituir o loop foreach pelo while ,
O valor de
i
não é alterado no bloco if porque você deseja obter o valor do novo item DO MESMO ÍNDICE, depois que o item antigo for excluído.fonte
Você pode tentar fazer o loop inverso, para que some_list faça algo como:
Dessa forma, o índice está alinhado e não sofre as atualizações da lista (independentemente de você exibir o elemento atual ou não).
fonte
reversed(list(enumerate(some_list)))
seria mais simples do que calcular os índices por conta própria.Uma solução possível, útil se você deseja não apenas remover algumas coisas, mas também fazer algo com todos os elementos em um único loop:
fonte
bad
coisas, fazer algo com isso e também fazer algo comgood
coisas em um loop?alist[:]
). E como você pode estar fazendo algo sofisticado, ele realmente tem um caso de uso. Boa revisão é boa. Pegue o meu voto.Eu precisava fazer algo semelhante e, no meu caso, o problema era a memória - eu precisava mesclar vários objetos de conjunto de dados em uma lista, depois de fazer algumas coisas com eles, como um novo objeto, e precisava me livrar de cada entrada na qual eu estava mesclando evite duplicar todos eles e aumentar a memória. No meu caso, ter os objetos em um dicionário em vez de uma lista funcionou bem:
`` ``
`` ``
fonte
TLDR:
Eu escrevi uma biblioteca que permite que você faça isso:
É melhor usar outro método, se possível, que não exija a modificação do iterável durante a iteração, mas, para alguns algoritmos, pode não ser tão simples assim. E, portanto, se você tiver certeza de que deseja realmente o padrão de código descrito na pergunta original, é possível.
Deve funcionar em todas as seqüências mutáveis, não apenas nas listas.
Resposta completa:
Editar: o último exemplo de código nesta resposta fornece um caso de uso do motivo pelo qual às vezes você pode modificar uma lista no lugar em vez de usar uma compreensão da lista. A primeira parte das respostas serve como tutorial de como uma matriz pode ser modificada no local.
A solução segue a partir deste resposta (para uma pergunta relacionada) do remetente. O que explica como o índice da matriz é atualizado durante a iteração em uma lista que foi modificada. A solução abaixo foi projetada para rastrear corretamente o índice da matriz, mesmo que a lista seja modificada.
Baixar
fluidIter.py
a partir daquihttps://github.com/alanbacon/FluidIterator
, é apenas um único arquivo assim não há necessidade de instalar git. Como não há instalador, você precisará garantir que o arquivo esteja no caminho python. O código foi escrito para o python 3 e não foi testado no python 2.Isso produzirá a seguinte saída:
Acima, usamos o
pop
método no objeto da lista de fluidos. Outros métodos iteráveis comuns também são implementadas, comodel fluidL[i]
,.remove
,.insert
,.append
,.extend
. A lista também pode ser modificada usando fatias (sort
ereverse
métodos não são implementados).A única condição é que você deve modificar a lista apenas no local, se em algum momento
fluidL
ou tiverl
sido reatribuído para um objeto de lista diferente, o código não funcionaria. OfluidL
objeto original ainda seria usado pelo loop for, mas ficaria fora do escopo para modificação.ie
Se quisermos acessar o valor atual do índice da lista, não podemos usar enumerar, pois isso conta apenas quantas vezes o loop for foi executado. Em vez disso, usaremos o objeto iterador diretamente.
Isso produzirá o seguinte:
A
FluidIterable
classe apenas fornece um wrapper para o objeto de lista original. O objeto original pode ser acessado como uma propriedade do objeto fluido da seguinte forma:Mais exemplos / testes podem ser encontrados na
if __name__ is "__main__":
seção na parte inferior defluidIter.py
. Vale a pena analisar porque explicam o que acontece em várias situações. Como: Substituir grandes seções da lista usando uma fatia. Ou usando (e modificando) o mesmo iterável em loops aninhados.Como afirmei no início: esta é uma solução complicada que prejudicará a legibilidade do seu código e dificultará a depuração. Portanto, outras soluções, como as compreensões de lista mencionadas na resposta de David Raznick, devem ser consideradas primeiro. Dito isto, encontrei momentos em que essa classe me foi útil e mais fácil de usar do que acompanhar os índices de elementos que precisam ser excluídos.
Edit: Como mencionado nos comentários, esta resposta realmente não apresenta um problema para o qual essa abordagem fornece uma solução. Vou tentar abordar isso aqui:
A compreensão da lista fornece uma maneira de gerar uma nova lista, mas essas abordagens tendem a olhar para cada elemento isoladamente, em vez do estado atual da lista como um todo.
ie
Mas e se o resultado do
testFunc
depende dos elementos que já foram adicionadosnewList
? Ou os elementos ainda emoldList
podem ser adicionados a seguir? Ainda pode haver uma maneira de usar uma compreensão de lista, mas ela começará a perder sua elegância e, para mim, é mais fácil modificar uma lista no local.O código abaixo é um exemplo de algoritmo que sofre do problema acima. O algoritmo reduzirá uma lista para que nenhum elemento seja múltiplo de qualquer outro elemento.
A saída e a lista reduzida final são mostradas abaixo
fonte
some_list[:] = [x for x in some_list if not some_condition(x)]
não alcança? Sem uma resposta para isso, por que alguém deveria acreditar que baixar e usar sua biblioteca de 600 linhas completa com erros de digitação e código comentado é uma solução melhor para o problema deles do que o one-liner? -1.some_list[:] = [x for x in some_list if not some_condition(y)]
ondey
é um elemento de lista diferentex
. Nem seria possível escreversome_list[:] = [x for x in some_list if not some_condition(intermediateStateOf_some_list)]
.O método mais eficaz é compreensão da lista, muitas pessoas mostram seu caso, é claro, também é uma boa maneira de obter um
iterator
meiofilter
.Há um exemplo (obtenha as probabilidades na tupla):
Cuidado: Você também não pode manipular iteradores. Às vezes, os iteradores são melhores que as seqüências.
fonte
O loop for será repetido pelo índice ..
considere que você tem uma lista,
você está usando a variável de lista chamada
lis
. e você está usando o mesmo para remover ..sua variável
durante a 5ª iteração,
seu número 35 não era primo, então você o removeu de uma lista.
e o próximo valor (65) passa para o índice anterior.
então a quarta iteração executada apontou para a quinta.
é por isso que seu loop não cobre 65, pois foi movido para o índice anterior.
portanto, você não deve referenciar a lista em outra variável que ainda faça referência ao original em vez de copiar.
o mesmo acontece com a cópia da lista usando
list[::]
agora você vai dar,
O problema é que você removeu um valor de uma lista durante a iteração e o índice da lista será recolhido.
então você pode tentar entender.
que suporta todos os iteráveis como, lista, tupla, ditado, string etc.
fonte
Se você deseja excluir elementos de uma lista durante a iteração, use um loop while para alterar o índice atual e o índice final após cada exclusão.
Exemplo:
fonte
As outras respostas estão corretas: geralmente é uma má idéia excluir de uma lista que você está iterando. A iteração reversa evita as armadilhas, mas é muito mais difícil seguir o código que faz isso; portanto, geralmente é melhor usar uma compreensão de lista ou
filter
.No entanto, há um caso em que é seguro remover elementos de uma sequência que você está iterando: se você estiver removendo apenas um item enquanto estiver iterando. Isso pode ser garantido usando a
return
ou abreak
. Por exemplo:Isso geralmente é mais fácil de entender do que a compreensão de uma lista quando você está realizando algumas operações com efeitos colaterais no primeiro item de uma lista que atenda a alguma condição e removendo esse item da lista imediatamente depois.
fonte
Eu posso pensar em três abordagens para resolver seu problema. Como exemplo, vou criar uma lista aleatória de tuplas
somelist = [(1,2,3), (4,5,6), (3,6,6), (7,8,9), (15,0,0), (10,11,12)]
. A condição que eu escolho ésum of elements of a tuple = 15
. Na lista final, teremos apenas as tuplas cuja soma não é igual a 15.O que eu escolhi é um exemplo escolhido aleatoriamente. Sinta-se livre para alterar a lista de tuplas e a condição que eu escolhi.
Método 1.> Use a estrutura que você sugeriu (onde uma preenche um código dentro de um loop for). Eu uso um código pequeno com
del
para excluir uma tupla que atenda à referida condição. No entanto, este método perderá uma tupla (que satisfaça a referida condição) se duas tuplas colocadas consecutivamente atenderem à condição especificada.Método 2.> Construa uma nova lista que contenha elementos (tuplas) onde a condição especificada não é atendida (isso é o mesmo que remover elementos da lista em que a condição especificada é atendida). A seguir está o código para isso:
Método 3.> Encontre índices onde a condição especificada é atendida e use os elementos remover (tuplas) correspondentes a esses índices. A seguir está o código para isso.
O método 1 e o método 2 são mais rápidos que o método 3 . O método2 e o método3 são mais eficientes que o método1. Eu prefiro o method2 . Para o exemplo mencionado acima,
time(method1) : time(method2) : time(method3) = 1 : 1 : 1.7
fonte
Para qualquer coisa que possa ser realmente grande, eu uso o seguinte.
Isso deve ser significativamente mais rápido do que qualquer outra coisa.
fonte
Em algumas situações, em que você está fazendo mais do que simplesmente filtrar um item de uma lista por vez, deseja que sua iteração seja alterada durante a iteração.
Aqui está um exemplo em que copiar a lista antecipadamente está incorreto, a iteração reversa é impossível e a compreensão da lista também não é uma opção.
fonte
Se você usar a nova lista posteriormente, poderá simplesmente definir o elem como Nenhum e depois julgá-lo no ciclo posterior, como este
Dessa forma, você não precisa copiar a lista e é mais fácil de entender.
fonte
coloque uma lista de números e você deseja remover todos os números que são divisíveis por 3,
usando
list comprehension
, isso criará uma nova lista e criará um novo espaço de memóriausando a
lambda filter
função, isso criará uma nova lista resultante e consumirá espaço de memóriasem consumir espaço de memória para nova lista e modificar lista existente
fonte