Domanda

Sto ripetendo un elenco di tuple in Python e sto tentando di rimuoverle se soddisfano determinati criteri.

for tup in somelist:
    if determine(tup):
         code_to_remove_tup

Cosa devo usare al posto di code_to_remove_tup ? Non riesco a capire come rimuovere l'oggetto in questo modo.

È stato utile?

Soluzione

Puoi utilizzare la comprensione di un elenco per creare un nuovo elenco contenente solo gli elementi che non desideri rimuovere:

somelist = [x for x in somelist if not determine(x)]

Oppure, assegnando alla sezione somelist [:] , puoi mutare l'elenco esistente per contenere solo gli elementi che desideri:

somelist[:] = [x for x in somelist if not determine(x)]

Questo approccio potrebbe essere utile se ci sono altri riferimenti a somelist che devono riflettere le modifiche.

Invece di una comprensione, puoi anche usare itertools . In Python 2:

from itertools import ifilterfalse
somelist[:] = ifilterfalse(determine, somelist)

O in Python 3:

from itertools import filterfalse
somelist[:] = filterfalse(determine, somelist)

Altri suggerimenti

Le risposte che suggeriscono la comprensione dell'elenco sono QUASI corrette - tranne per il fatto che costruiscono un elenco completamente nuovo e quindi gli danno lo stesso nome del vecchio elenco, NON modificano il vecchio elenco in atto. È diverso da quello che faresti con la rimozione selettiva, come in @ suggerimento di Lennart - è più veloce, ma se il tuo L'elenco è accessibile tramite più riferimenti il ??fatto che stai semplicemente riposizionando uno dei riferimenti e NON alterando l'oggetto elenco stesso può portare a bug sottili e disastrosi.

Fortunatamente, è estremamente facile ottenere sia la velocità di comprensione dell'elenco sia la semantica richiesta dell'alterazione sul posto - basta codificare:

somelist[:] = [tup for tup in somelist if determine(tup)]

Nota la sottile differenza con le altre risposte: questa NON sta assegnando a un nome a barre - sta assegnando a una sezione di elenco che sembra essere l'intero elenco, sostituendo così l'elenco contenuto all'interno dello stesso oggetto elenco Python , anziché semplicemente riposizionare un riferimento (dall'oggetto elenco precedente al nuovo oggetto elenco) come le altre risposte.

Devi prendere una copia dell'elenco e iterare prima su di esso, altrimenti l'iterazione fallirà con risultati che potrebbero essere inaspettati.

Ad esempio (dipende dal tipo di elenco):

for tup in somelist[:]:
    etc....

Un esempio:

>>> somelist = range(10)
>>> for x in somelist:
...     somelist.remove(x)
>>> somelist
[1, 3, 5, 7, 9]

>>> somelist = range(10)
>>> for x in somelist[:]:
...     somelist.remove(x)
>>> somelist
[]
for i in range(len(somelist) - 1, -1, -1):
    if some_condition(somelist, i):
        del somelist[i]

Devi andare indietro, altrimenti è un po 'come segare il ramo su cui sei seduto :-)

Utenti Python 2: sostituisci range con xrange per evitare di creare un elenco hardcoded

Il tuo miglior approccio per un tale esempio sarebbe una comprensione dell'elenco

somelist = [tup for tup in somelist if determine(tup)]

Nei casi in cui stai facendo qualcosa di più complesso rispetto alla chiamata di una funzione determin , preferisco costruire un nuovo elenco e semplicemente aggiungerlo mentre procedo. Ad esempio

newlist = []
for tup in somelist:
    # lots of code here, possibly setting things up for calling determine
    if determine(tup):
        newlist.append(tup)
somelist = newlist

La copia dell'elenco usando rimuovi potrebbe rendere il tuo codice un po 'più pulito, come descritto in una delle risposte di seguito. Non dovresti assolutamente farlo per elenchi estremamente grandi, poiché ciò comporta prima di tutto la copia dell'intero elenco e anche l'esecuzione di un'operazione O (n) remove per ogni elemento rimosso, rendendolo un algoritmo O (n ^ 2) .

for tup in somelist[:]:
    # lots of code here, possibly setting things up for calling determine
    if determine(tup):
        newlist.append(tup)

Tutorial ufficiale di Python 2 4.2. " per Dichiarazioni "

https://docs.python.org/2/tutorial /controlflow.html#for-statements

Questa parte della documentazione chiarisce che:

  • è necessario creare una copia dell'elenco ripetuto per modificarlo
  • un modo per farlo è con la notazione di sezione [:[
  

Se è necessario modificare la sequenza su cui si sta ripetendo l'iter all'interno del loop (ad esempio per duplicare gli elementi selezionati), si consiglia di effettuare prima una copia. L'iterazione su una sequenza non crea implicitamente una copia. La notazione delle sezioni rende questo particolarmente conveniente:

>>> words = ['cat', 'window', 'defenestrate']
>>> for w in words[:]:  # Loop over a slice copy of the entire list.
...     if len(w) > 6:
...         words.insert(0, w)
...
>>> words
['defenestrate', 'cat', 'window', 'defenestrate']

Documentazione Python 2 7.3. " La dichiarazione for "

https://docs.python.org/2/reference/compound_stmts # .html per

Questa parte della documentazione afferma ancora una volta che è necessario effettuare una copia e fornisce un esempio di rimozione effettiva:

  

Nota: c'è una sottigliezza quando la sequenza viene modificata dal loop (ciò può avvenire solo per sequenze mutabili, ovvero elenchi). Un contatore interno viene utilizzato per tenere traccia di quale elemento viene utilizzato successivamente, e questo viene incrementato ad ogni iterazione. Quando questo contatore ha raggiunto la lunghezza della sequenza, il loop termina. Ciò significa che se la suite elimina l'elemento corrente (o precedente) dalla sequenza, l'elemento successivo verrà ignorato (poiché ottiene l'indice dell'elemento corrente che è già stato trattato). Allo stesso modo, se la suite inserisce un elemento nella sequenza prima dell'elemento corrente, l'elemento corrente verrà nuovamente trattato la volta successiva attraverso il ciclo. Questo può portare a cattivi bug che possono essere evitati facendo una copia temporanea usando una porzione dell'intera sequenza, ad es.

for x in a[:]:
    if x < 0: a.remove(x)

Tuttavia, non sono d'accordo con questa implementazione, poiché .remove () deve iterare l'intero elenco per trovare il valore.

Invece:

Generalmente vuoi semplicemente scegliere l'opzione .append () più veloce per impostazione predefinita, a meno che la memoria non sia un grosso problema.

Python potrebbe fare di meglio?

Sembra che questa particolare API Python possa essere migliorata. Confrontalo, ad esempio, con la sua controparte Java ListIterator , che chiarisce chiaramente che non è possibile modificare un elenco in fase di iterazione se non con lo stesso iteratore e offre modi efficaci per farlo senza copiare l'elenco.

Forse la logica di fondo è che si presume che gli elenchi di Python siano supportati da array dinamici, e quindi qualsiasi tipo di rimozione sarà comunque inefficiente nel tempo, mentre Java ha una gerarchia di interfaccia migliore con entrambi ArrayList e LinkedList implementazioni di ListIterator .

Non sembra esserci nemmeno un tipo di elenco di collegamenti espliciti nello stdlib di Python: Elenco di collegamenti Python

Per coloro a cui piace la programmazione funzionale:

somelist[:] = filter(lambda tup: not determine(tup), somelist)

o

from itertools import ifilterfalse
somelist[:] = list(ifilterfalse(determine, somelist))

Potrebbe anche essere intelligente creare anche un nuovo elenco se l'elemento dell'elenco corrente soddisfa i criteri desiderati.

così:

for item in originalList:
   if (item != badValue):
        newList.append(item)

e per evitare di dover ricodificare l'intero progetto con il nome della nuova lista:

originalList[:] = newList

nota, dalla documentazione di Python:

  

copy.copy (x)   Restituisce una copia superficiale di x.

     

copy.deepcopy (x)   Restituisce una copia profonda di x.

Avevo bisogno di farlo con un elenco enorme e duplicare l'elenco sembrava costoso, soprattutto perché nel mio caso il numero di eliminazioni sarebbe stato ridotto rispetto agli elementi rimasti. Ho adottato questo approccio di basso livello.

array = [lots of stuff]
arraySize = len(array)
i = 0
while i < arraySize:
    if someTest(array[i]):
        del array[i]
        arraySize -= 1
    else:
        i += 1

Quello che non so è quanto siano efficienti un paio di eliminazioni rispetto alla copia di un grande elenco. Per favore commenta se hai qualche idea.

Questa risposta è stata originariamente scritta in risposta a una domanda che da allora è stata contrassegnata come duplicata: Rimozione delle coordinate dall'elenco su Python

Esistono due problemi nel codice:

1) Quando si utilizza remove (), si tenta di rimuovere numeri interi mentre è necessario rimuovere una tupla.

2) Il ciclo for salterà gli elementi nell'elenco.

Esaminiamo cosa succede quando eseguiamo il tuo codice:

>>> L1 = [(1,2), (5,6), (-1,-2), (1,-2)]
>>> for (a,b) in L1:
...   if a < 0 or b < 0:
...     L1.remove(a,b)
... 
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
TypeError: remove() takes exactly one argument (2 given)

Il primo problema è che stai rimuovendo 'a' e 'b' per rimuovere (), ma remove () accetta solo un singolo argomento. Quindi, come possiamo ottenere remove () per funzionare correttamente con il tuo elenco? Dobbiamo capire qual è ogni elemento del tuo elenco. In questo caso, ognuno è una tupla. Per vedere questo, accediamo a un elemento dell'elenco (l'indicizzazione inizia da 0):

>>> L1[1]
(5, 6)
>>> type(L1[1])
<type 'tuple'>

Aha! Ogni elemento di L1 è in realtà una tupla. Quindi è quello che dobbiamo passare per rimuovere (). Le tuple in pitone sono molto semplici, sono semplicemente create racchiudendo i valori tra parentesi. "a, b" non è una tupla, ma "a, b)" è una tupla. Quindi modifichiamo il codice ed eseguiamo di nuovo:

# The remove line now includes an extra "()" to make a tuple out of "a,b"
L1.remove((a,b))

Questo codice viene eseguito senza errori, ma diamo un'occhiata all'elenco che genera:

L1 is now: [(1, 2), (5, 6), (1, -2)]

Perché (1, -2) è ancora nella tua lista? Si scopre che modificare l'elenco mentre si utilizza un ciclo per scorrere su di esso è una pessima idea senza cure particolari. Il motivo per cui (1, -2) rimane nell'elenco è che le posizioni di ciascun elemento all'interno dell'elenco sono cambiate tra le iterazioni del ciclo for. Diamo un'occhiata a cosa succede se inseriamo nel codice sopra un elenco più lungo:

L1 = [(1,2),(5,6),(-1,-2),(1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
### Outputs:
L1 is now: [(1, 2), (5, 6), (1, -2), (3, 4), (5, 7), (2, 1), (5, -1), (0, 6)]

Come puoi dedurre da quel risultato, ogni volta che l'istruzione condizionale restituisce true e viene rimosso un elemento dell'elenco, la successiva iterazione del ciclo salterà la valutazione dell'elemento successivo nell'elenco perché i suoi valori si trovano ora in indici diversi.

La soluzione più intuitiva è copiare l'elenco, quindi scorrere l'elenco originale e modificare solo la copia. Puoi provare a farlo in questo modo:

L2 = L1
for (a,b) in L1:
    if a < 0 or b < 0 :
        L2.remove((a,b))
# Now, remove the original copy of L1 and replace with L2
print L2 is L1
del L1
L1 = L2; del L2
print ("L1 is now: ", L1)

Tuttavia, l'output sarà identico a prima:

'L1 is now: ', [(1, 2), (5, 6), (1, -2), (3, 4), (5, 7), (2, 1), (5, -1), (0, 6)]

Questo perché quando abbiamo creato L2, Python non ha effettivamente creato un nuovo oggetto. Invece, faceva semplicemente riferimento a L2 allo stesso oggetto di L1. Possiamo verificarlo con 'is' che è diverso dal semplice "uguale a" (==).

>>> L2=L1
>>> L1 is L2
True

Possiamo fare una vera copia usando copy.copy (). Quindi tutto funziona come previsto:

import copy
L1 = [(1,2), (5,6),(-1,-2), (1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
L2 = copy.copy(L1)
for (a,b) in L1:
    if a < 0 or b < 0 :
        L2.remove((a,b))
# Now, remove the original copy of L1 and replace with L2
del L1
L1 = L2; del L2
>>> L1 is now: [(1, 2), (5, 6), (3, 4), (5, 7), (2, 1), (0, 6)]

Infine, esiste una soluzione più pulita che dover fare una copia completamente nuova di L1. La funzione reversed ():

L1 = [(1,2), (5,6),(-1,-2), (1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
for (a,b) in reversed(L1):
    if a < 0 or b < 0 :
        L1.remove((a,b))
print ("L1 is now: ", L1)
>>> L1 is now: [(1, 2), (5, 6), (3, 4), (5, 7), (2, 1), (0, 6)]

Sfortunatamente, non posso descrivere adeguatamente come funziona reversed (). Restituisce un oggetto 'listreverseiterator' quando gli viene passato un elenco. Ai fini pratici, puoi pensarlo come creare una copia rovesciata del suo argomento. Questa è la soluzione che raccomando.

Se vuoi fare qualcos'altro durante l'iterazione, può essere utile ottenere sia l'indice (che ti garantisce di poterlo fare riferimento, ad esempio se hai un elenco di dicts) sia il contenuto effettivo dell'elemento dell'elenco.

inlist = [{'field1':10, 'field2':20}, {'field1':30, 'field2':15}]    
for idx, i in enumerate(inlist):
    do some stuff with i['field1']
    if somecondition:
        xlist.append(idx)
for i in reversed(xlist): del inlist[i]

enumerate ti dà accesso all'elemento e all'indice contemporaneamente. invertito è in modo che gli indici che eliminerai in seguito non cambino su di te.

Potresti voler usare filter () disponibile come integrato.

Per maggiori dettagli controlla qui

Puoi provare a eseguire il ciclo continuo al contrario, quindi per some_list farai qualcosa del tipo:

list_len = len(some_list)
for i in range(list_len):
    reverse_i = list_len - 1 - i
    cur = some_list[reverse_i]

    # some logic with cur element

    if some_condition:
        some_list.pop(reverse_i)

In questo modo l'indice è allineato e non risente degli aggiornamenti dell'elenco (indipendentemente dal fatto che si Pop Pop o meno).

Una possibile soluzione, utile se si desidera non solo rimuovere alcune cose, ma anche fare qualcosa con tutti gli elementi in un singolo ciclo:

alist = ['good', 'bad', 'good', 'bad', 'good']
i = 0
for x in alist[:]:
    if x == 'bad':
        alist.pop(i)
        i -= 1
    # do something cool with x or just print x
    print(x)
    i += 1

La maggior parte delle risposte qui vuole che tu crei una copia dell'elenco. Ho avuto un caso d'uso in cui l'elenco era piuttosto lungo (110.000 articoli) ed era invece più intelligente continuare a ridurre l'elenco.

Prima di tutto dovrai sostituire ogni ciclo foreach con while loop ,

i = 0
while i < len(somelist):
    if determine(somelist[i]):
         del somelist[i]
    else:
        i += 1

Il valore di i non viene modificato nel blocco if perché vorrai ottenere il valore del nuovo elemento DALLO STESSO INDICE, una volta eliminato il vecchio elemento.

Avevo bisogno di fare qualcosa di simile e nel mio caso il problema era la memoria: avevo bisogno di unire più oggetti del set di dati all'interno di un elenco, dopo aver fatto alcune cose con loro, come un nuovo oggetto, e dovevo liberarmi di ogni voce I si stava unendo per evitare di duplicare tutti e far esplodere la memoria. Nel mio caso, avere gli oggetti in un dizionario anziché in un elenco ha funzionato bene:

`` `

k = range(5)
v = ['a','b','c','d','e']
d = {key:val for key,val in zip(k, v)}

print d
for i in range(5):
    print d[i]
    d.pop(i)
print d

`` `

TLDR:

Ho scritto una libreria che ti consente di farlo:

from fluidIter import FluidIterable
fSomeList = FluidIterable(someList)  
for tup in fSomeList:
    if determine(tup):
        # remove 'tup' without "breaking" the iteration
        fSomeList.remove(tup)
        # tup has also been removed from 'someList'
        # as well as 'fSomeList'

È preferibile utilizzare un altro metodo, se possibile, che non richiede la modifica dell'iterabile durante l'iterazione su di esso, ma per alcuni algoritmi potrebbe non essere così semplice. E quindi se sei sicuro di volere davvero il modello di codice descritto nella domanda originale, è possibile.

Dovrebbe funzionare su tutte le sequenze mutabili non solo sugli elenchi.


Risposta completa:

Modifica: l'ultimo esempio di codice in questa risposta fornisce un caso d'uso per perché a volte potresti voler modificare un elenco in luogo piuttosto che utilizzare una comprensione dell'elenco. La prima parte delle risposte funge da tutorial di how un array può essere modificato sul posto.

La soluzione fa seguito alla questa risposta (per una domanda correlata) del mittente. Il che spiega come viene aggiornato l'indice dell'array durante l'iterazione di un elenco che è stato modificato. La soluzione seguente è progettata per tracciare correttamente l'indice dell'array anche se l'elenco viene modificato.

Scarica fluidIter.py da qui https: / /github.com/alanbacon/FluidIterator , è solo un singolo file, quindi non è necessario installare git. Non esiste un programma di installazione, quindi sarà necessario assicurarsi che il file si trovi nel percorso Python. Il codice è stato scritto per Python 3 ed è non testato su Python 2.

from fluidIter import FluidIterable
l = [0,1,2,3,4,5,6,7,8]  
fluidL = FluidIterable(l)                       
for i in fluidL:
    print('initial state of list on this iteration: ' + str(fluidL)) 
    print('current iteration value: ' + str(i))
    print('popped value: ' + str(fluidL.pop(2)))
    print(' ')

print('Final List Value: ' + str(l))

Questo produrrà il seguente output:

initial state of list on this iteration: [0, 1, 2, 3, 4, 5, 6, 7, 8]
current iteration value: 0
popped value: 2

initial state of list on this iteration: [0, 1, 3, 4, 5, 6, 7, 8]
current iteration value: 1
popped value: 3

initial state of list on this iteration: [0, 1, 4, 5, 6, 7, 8]
current iteration value: 4
popped value: 4

initial state of list on this iteration: [0, 1, 5, 6, 7, 8]
current iteration value: 5
popped value: 5

initial state of list on this iteration: [0, 1, 6, 7, 8]
current iteration value: 6
popped value: 6

initial state of list on this iteration: [0, 1, 7, 8]
current iteration value: 7
popped value: 7

initial state of list on this iteration: [0, 1, 8]
current iteration value: 8
popped value: 8

Final List Value: [0, 1]

Sopra abbiamo usato il metodo pop sull'oggetto lista fluidi. Altri metodi iterabili comuni sono anche implementati come del fluidL [i] , .remove , .insert , .append , .extend . L'elenco può anche essere modificato usando le sezioni (i metodi sort e reverse non sono implementati).

L'unica condizione è che è necessario modificare l'elenco in atto solo se in qualsiasi momento fluidL o l fossero riassegnati a un oggetto elenco diverso, il codice non funzionerebbe . L'oggetto fluidL originale verrebbe comunque utilizzato dal ciclo for ma diventerebbe fuori portata per noi da modificare.

cioè.

fluidL[2] = 'a'   # is OK
fluidL = [0, 1, 'a', 3, 4, 5, 6, 7, 8]  # is not OK

Se vogliamo accedere al valore dell'indice corrente dell'elenco non possiamo usare enumerare, poiché questo conta solo quante volte è stato eseguito il ciclo for. Invece useremo direttamente l'oggetto iteratore.

fluidArr = FluidIterable([0,1,2,3])
# get iterator first so can query the current index
fluidArrIter = fluidArr.__iter__()
for i, v in enumerate(fluidArrIter):
    print('enum: ', i)
    print('current val: ', v)
    print('current ind: ', fluidArrIter.currentIndex)
    print(fluidArr)
    fluidArr.insert(0,'a')
    print(' ')

print('Final List Value: ' + str(fluidArr))

Questo produrrà il seguente:

enum:  0
current val:  0
current ind:  0
[0, 1, 2, 3]

enum:  1
current val:  1
current ind:  2
['a', 0, 1, 2, 3]

enum:  2
current val:  2
current ind:  4
['a', 'a', 0, 1, 2, 3]

enum:  3
current val:  3
current ind:  6
['a', 'a', 'a', 0, 1, 2, 3]

Final List Value: ['a', 'a', 'a', 'a', 0, 1, 2, 3]

La classe FluidIterable fornisce solo un wrapper per l'oggetto elenco originale. È possibile accedere all'oggetto originale come una proprietà dell'oggetto fluido in questo modo:

originalList = fluidArr.fixedIterable

Altri esempi / test sono disponibili nella se __name__ è " __ main __ " ;: nella parte inferiore di fluidIter.py . Vale la pena guardarli perché spiegano cosa succede in varie situazioni. Come ad esempio: sostituire una grande sezione dell'elenco usando una sezione. O usando (e modificando) lo stesso iterabile in loop annidati per

Come ho detto per cominciare: questa è una soluzione complicata che danneggerà la leggibilità del tuo codice e renderà più difficile il debug. Pertanto, altre soluzioni come la comprensione dell'elenco menzionate nella risposta di David Raznick dovrebbero essere considerate per prime. Detto questo, ho trovato momenti in cui questa classe mi è stata utile ed è stata più facile da usare che tenere traccia degli indici degli elementi che devono essere eliminati.


Modifica: come menzionato nei commenti, questa risposta non presenta realmente un problema per il quale questo approccio fornisce una soluzione. Proverò ad affrontarlo qui:

La comprensione dell'elenco fornisce un modo per generare un nuovo elenco, ma questi approcci tendono a considerare ogni elemento in isolamento piuttosto che lo stato attuale dell'elenco nel suo insieme.

cioè.

newList = [i for i in oldList if testFunc(i)]

Ma cosa succede se il risultato di testFunc dipende dagli elementi che sono già stati aggiunti a newList ? O gli elementi ancora in oldList che potrebbero essere aggiunti successivamente? Potrebbe esserci ancora un modo per utilizzare la comprensione di un elenco, ma inizierà a perdere la sua eleganza e per me è più facile modificare un elenco in atto.

Il codice seguente è un esempio di un algoritmo che soffre del problema precedente. L'algoritmo ridurrà un elenco in modo che nessun elemento sia multiplo di qualsiasi altro elemento.

randInts = [70, 20, 61, 80, 54, 18, 7, 18, 55, 9]
fRandInts = FluidIterable(randInts)
fRandIntsIter = fRandInts.__iter__()
# for each value in the list (outer loop)
# test against every other value in the list (inner loop)
for i in fRandIntsIter:
    print(' ')
    print('outer val: ', i)
    innerIntsIter = fRandInts.__iter__()
    for j in innerIntsIter:
        innerIndex = innerIntsIter.currentIndex
        # skip the element that the outloop is currently on
        # because we don't want to test a value against itself
        if not innerIndex == fRandIntsIter.currentIndex:
            # if the test element, j, is a multiple 
            # of the reference element, i, then remove 'j'
            if j%i == 0:
                print('remove val: ', j)
                # remove element in place, without breaking the
                # iteration of either loop
                del fRandInts[innerIndex]
            # end if multiple, then remove
        # end if not the same value as outer loop
    # end inner loop
# end outerloop

print('')
print('final list: ', randInts)

L'output e l'elenco ridotto finale sono mostrati di seguito

outer val:  70

outer val:  20
remove val:  80

outer val:  61

outer val:  54

outer val:  18
remove val:  54
remove val:  18

outer val:  7
remove val:  70

outer val:  55

outer val:  9
remove val:  18

final list:  [20, 61, 7, 55, 9]

Le altre risposte sono corrette che di solito è una cattiva idea eliminare da un elenco che stai ripetendo. L'iterazione inversa evita le insidie, ma è molto più difficile seguire il codice che lo fa, quindi di solito è meglio usare una comprensione dell'elenco o un filtro .

Esiste, tuttavia, un caso in cui è sicuro rimuovere elementi da una sequenza che stai ripetendo: se rimuovi solo un elemento mentre stai ripetendo. Questo può essere garantito usando un return o un break . Ad esempio:

for i, item in enumerate(lst):
    if item % 4 == 0:
        foo(item)
        del lst[i]
        break

Questo è spesso più facile da comprendere di una comprensione dell'elenco quando si eseguono alcune operazioni con effetti collaterali sul primo elemento in un elenco che soddisfa alcune condizioni e quindi si rimuove tale elemento dall'elenco immediatamente dopo.

Il metodo più efficace è la comprensione dell'elenco, molte persone mostrano il loro caso, ovviamente, è anche un buon modo per ottenere un iteratore attraverso filter .

  

Filter riceve una funzione e una sequenza. Filter applica a turno la funzione passata a ciascun elemento, quindi decide se conservare o scartare l'elemento a seconda che il valore di ritorno della funzione sia True o False .

C'è un esempio (ottieni le probabilità nella tupla):

list(filter(lambda x:x%2==1, (1, 2, 4, 5, 6, 9, 10, 15)))  
# result: [1, 5, 9, 15]

Attenzione: non è inoltre possibile gestire gli iteratori. Gli iteratori sono talvolta migliori delle sequenze.

Posso pensare a tre approcci per risolvere il tuo problema. Ad esempio, creerò un elenco casuale di tuple somelist = [(1,2,3), (4,5,6), (3,6,6), (7,8,9), (15,0,0), (10,11,12)] . La condizione che scelgo è somma degli elementi di una tupla = 15 . Nell'elenco finale avremo solo quelle tuple la cui somma non è uguale a 15.

Quello che ho scelto è un esempio scelto a caso. Sentiti libero di cambiare l'elenco di tuple e la condizione che ho scelto.

Metodo 1. > Utilizza il framework che hai suggerito (dove si inserisce un codice all'interno di un ciclo for). Uso un piccolo codice con del per eliminare una tupla che soddisfa tale condizione. Tuttavia, questo metodo mancherà una tupla (che soddisfa tale condizione) se due tuple posizionate consecutivamente soddisfano la condizione data.

for tup in somelist:
    if ( sum(tup)==15 ): 
        del somelist[somelist.index(tup)]

print somelist
>>> [(1, 2, 3), (3, 6, 6), (7, 8, 9), (10, 11, 12)]

Metodo 2. . Di seguito è riportato il codice per questo:

newlist1 = [somelist[tup] for tup in range(len(somelist)) if(sum(somelist[tup])!=15)]

print newlist1
>>>[(1, 2, 3), (7, 8, 9), (10, 11, 12)]

Metodo 3. > Trova gli indici in cui è soddisfatta la condizione specificata, quindi usa rimuovi elementi (tuple) corrispondenti a quegli indici. Di seguito è riportato il codice per questo.

indices = [i for i in range(len(somelist)) if(sum(somelist[i])==15)]
newlist2 = [tup for j, tup in enumerate(somelist) if j not in indices]

print newlist2
>>>[(1, 2, 3), (7, 8, 9), (10, 11, 12)]

Il metodo 1 e il metodo 2 sono più veloci del metodo 3 . Method2 e method3 sono più efficienti di method1. Preferisco metodo2 . Per l'esempio di cui sopra, time (method1): time (method2): time (method3) = 1: 1: 1.7

per il ciclo verrà ripetuto tramite l'indice ..

considera di avere un elenco

[5, 7, 13, 29, 65, 91]

stai usando una variabile di lista chiamata lis . e stai usando lo stesso per rimuovere ..

la tua variabile

lis = [5, 7, 13, 29, 35, 65, 91]
       0  1   2   3   4   5   6

durante la quinta iterazione,

il tuo numero 35 non era un numero primo, quindi l'hai rimosso da un elenco.

lis.remove(y)

e quindi valore successivo (65) passano all'indice precedente.

lis = [5, 7, 13, 29, 65, 91]
       0  1   2   3   4   5

quindi il puntatore della 4a iterazione eseguita è passato al 5 ° ..

ecco perché il tuo loop non copre 65 da quando è stato spostato nell'indice precedente.

quindi non dovresti fare riferimento all'elenco in un'altra variabile che fa ancora riferimento all'originale anziché alla copia.

ite = lis #dont do it will reference instead copy

quindi copia dell'elenco usando list[::[

ora te lo darai,

[5, 7, 13, 29]

Il problema è che hai rimosso un valore da un elenco durante l'iterazione, quindi l'indice dell'elenco crollerà.

così puoi provare invece la comprensione.

che supporta tutti gli iterabili come, list, tuple, dict, string etc

Per tutto ciò che ha il potenziale per essere davvero grande, io uso il seguente.

import numpy as np

orig_list = np.array([1, 2, 3, 4, 5, 100, 8, 13])

remove_me = [100, 1]

cleaned = np.delete(orig_list, remove_me)
print(cleaned)

Dovrebbe essere significativamente più veloce di ogni altra cosa.

In alcune situazioni, in cui stai facendo molto di più che filtrare un elenco un elemento alla volta, vuoi che la tua iterazione cambi durante l'iterazione.

Ecco un esempio in cui la copia dell'elenco in anticipo non è corretta, l'iterazione inversa è impossibile e anche la comprensione dell'elenco non è un'opzione.

""" Sieve of Eratosthenes """

def generate_primes(n):
    """ Generates all primes less than n. """
    primes = list(range(2,n))
    idx = 0
    while idx < len(primes):
        p = primes[idx]
        for multiple in range(p+p, n, p):
            try:
                primes.remove(multiple)
            except ValueError:
                pass #EAFP
        idx += 1
        yield p

Se utilizzerai il nuovo elenco in un secondo momento, puoi semplicemente impostare elem su Nessuno e quindi giudicarlo nel ciclo successivo, in questo modo

for i in li:
    i = None

for elem in li:
    if elem is None:
        continue

In questo modo, non è necessario copiare l'elenco ed è più facile da capire.

sopra un elenco di numeri e si desidera rimuovere tutti i numeri che sono divisibili per 3,

list_number =[i for i in range(100)]

usando la comprensione dell'elenco , questo creerà un nuovo elenco e creerà nuovo spazio di memoria

new_list =[i for i in list_number if i%3!=0]

usando la funzione filtro lambda , questo creerà un nuovo elenco risultante e consumerà spazio memeory

new_list = list(filter(lambda x:x%3!=0, list_number))

senza consumare spazio di memoria per il nuovo elenco e modificare l'elenco esistente

for index, value in enumerate(list_number):
    if list_number[index]%3==0:
        list_number.remove(value)

Immediatamente vuoi creare una copia dell'elenco in modo da poterlo avere come riferimento quando stai iterando ed eliminando le tuple in quell'elenco che soddisfano determinati criteri.

Quindi dipende dal tipo di elenco desiderato per l'output che si tratti di un elenco delle tuple rimosse o di un elenco delle tuple che non vengono rimosse.

Come ha sottolineato David, raccomando la comprensione dell'elenco per mantenere gli elementi che non si desidera rimuovere.

somelist = [x for x in somelist if not determine(x)]
scroll top