Python Language Modifica della sequenza su cui stai iterando


Esempio

Un ciclo for itera su una sequenza, quindi alterare questa sequenza all'interno del ciclo potrebbe portare a risultati imprevisti (specialmente quando si aggiungono o rimuovono elementi):

alist = [0, 1, 2]
for index, value in enumerate(alist):
    alist.pop(index)
print(alist)
# Out: [1]

Nota: list.pop() viene utilizzato per rimuovere elementi dall'elenco.

Il secondo elemento non è stato cancellato perché l'iterazione attraversa gli indici in ordine. Il ciclo sopra riportato itera due volte, con i seguenti risultati:

# Iteration #1
index = 0
alist = [0, 1, 2]
alist.pop(0) # removes '0'

# Iteration #2
index = 1
alist = [1, 2]
alist.pop(1) # removes '2'

# loop terminates, but alist is not empty:
alist = [1]

Questo problema sorge perché gli indici stanno cambiando mentre si sta ripetendo nella direzione di aumentare l'indice. Per evitare questo problema, puoi scorrere il ciclo all'indietro :

alist = [1,2,3,4,5,6,7]
for index, item in reversed(list(enumerate(alist))):
    # delete all even items
    if item % 2 == 0:
        alist.pop(index)
print(alist)
# Out: [1, 3, 5, 7]

Eseguendo il ciclo dall'inizio alla fine, quando gli elementi vengono rimossi (o aggiunti), non influisce sugli indici delle voci precedenti nell'elenco. Quindi questo esempio rimuoverà correttamente tutti gli elementi che sono anche da alist .


Un problema simile si presenta quando si inseriscono o si aggiungono elementi a un elenco su cui si sta iterando , il che può generare un loop infinito:

alist = [0, 1, 2]
for index, value in enumerate(alist):
    # break to avoid infinite loop:
    if index == 20:     
        break           
    alist.insert(index, 'a')
print(alist)
# Out (abbreviated): ['a', 'a', ..., 'a', 'a',  0,   1,   2]

Senza la condizione di break , il ciclo inserirà 'a' fintanto che il computer non esaurirà la memoria e il programma potrà continuare. In una situazione come questa, in genere è preferibile creare un nuovo elenco e aggiungere elementi al nuovo elenco mentre si scorre l'elenco originale.


Quando si utilizza un ciclo for , non è possibile modificare gli elementi dell'elenco con la variabile placeholder :

alist = [1,2,3,4]
for item in alist:
    if item % 2 == 0:
        item = 'even'
print(alist)
# Out: [1,2,3,4]

Nell'esempio sopra, la modifica item non modifica nulla nella lista originale . È necessario utilizzare l'indice di lista ( alist[2] ) e enumerate() funziona bene per questo:

alist = [1,2,3,4]
for index, item in enumerate(alist):
    if item % 2 == 0:
        alist[index] = 'even'
print(alist)
# Out: [1, 'even', 3, 'even']

Un ciclo while potrebbe essere una scelta migliore in alcuni casi:

Se stai per eliminare tutti gli elementi nell'elenco:

zlist = [0, 1, 2]
while zlist:
    print(zlist[0])
    zlist.pop(0)
print('After: zlist =', zlist)

# Out: 0
#      1
#      2
# After: zlist = []

Sebbene la semplice reimpostazione di zlist lo stesso risultato;

zlist = []

L'esempio sopra può anche essere combinato con len() per fermarsi dopo un certo punto, o per eliminare tutti gli elementi tranne x nella lista:

zlist = [0, 1, 2]
x = 1
while len(zlist) > x:
    print(zlist[0])
    zlist.pop(0)
print('After: zlist =', zlist)

# Out: 0
#      1
# After: zlist = [2]

Oppure per scorrere un elenco durante l'eliminazione di elementi che soddisfano una determinata condizione (in questo caso l'eliminazione di tutti gli elementi pari):

zlist = [1,2,3,4,5]
i = 0
while i < len(zlist):
    if zlist[i] % 2 == 0:
        zlist.pop(i)
    else:
        i += 1
print(zlist)
# Out: [1, 3, 5]

Si noti che non si incrementa i dopo aver eliminato un elemento. zlist[i] l'elemento su zlist[i] , l'indice dell'elemento successivo è diminuito di uno, quindi controllando zlist[i] con lo stesso valore per i alla successiva iterazione, si verificherà correttamente l'elemento successivo nell'elenco .


Un modo contrario di pensare a rimuovere elementi indesiderati da una lista è aggiungere elementi desiderati a una nuova lista . L'esempio seguente è un'alternativa al secondo esempio di ciclo while :

zlist = [1,2,3,4,5]

z_temp = []
for item in zlist:
    if item % 2 != 0:
        z_temp.append(item)
zlist = z_temp
print(zlist)
# Out: [1, 3, 5]

Qui stiamo incanalando i risultati desiderati in una nuova lista. Possiamo quindi riassegnare facoltativamente l'elenco temporaneo alla variabile originale.

Con questa tendenza del pensiero, puoi invocare una delle più eleganti e potenti funzionalità di Python, la list comprehensions , che elimina le liste temporanee e le divergenze dall'ideologia sul listino sul posto / sull'indice degli indici.

zlist = [1,2,3,4,5]
[item for item in zlist if item % 2 != 0]
# Out: [1, 3, 5]