Come evitare l'errore "RuntimeError: il dizionario ha modificato la dimensione durante l'iterazione"?


258

Ho verificato tutte le altre domande con lo stesso errore ma non ho trovato alcuna soluzione utile = /

Ho un dizionario di elenchi:

d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}

in cui alcuni dei valori sono vuoti. Alla fine della creazione di questi elenchi, desidero rimuovere questi elenchi vuoti prima di restituire il mio dizionario. Attualmente sto provando a fare questo come segue:

for i in d:
    if not d[i]:
        d.pop(i)

tuttavia, questo mi sta dando l'errore di runtime. Sono consapevole che non è possibile aggiungere / rimuovere elementi in un dizionario mentre si scorre attraverso di esso ... quale sarebbe un modo per aggirare questo?

Risposte:


455

In Python 2.x la chiamata keyscrea una copia della chiave su cui è possibile scorrere durante la modifica di dict:

for i in d.keys():

Nota che questo non funziona in Python 3.x perché keysrestituisce un iteratore invece di un elenco.

Un altro modo è usare listper forzare una copia delle chiavi da realizzare. Questo funziona anche in Python 3.x:

for i in list(d):

1
Credo che intendevi "chiamare keyscrea una copia delle chiavi su cui puoi scorrere", ovvero le pluralchiavi, giusto? Altrimenti come si può iterare su una singola chiave? A proposito, non sto prendendo in
— giro

6
O tupla invece dell'elenco poiché è più veloce.
— Brambor,

8
Per chiarire il comportamento di Python 3.x, d.keys () restituisce un iterabile (non un iteratore), il che significa che è una vista direttamente sulle chiavi del dizionario. L'uso in for i in d.keys()realtà funziona in Python 3.x in generale , ma poiché sta iterando su una vista iterabile delle chiavi del dizionario, la chiamata d.pop()durante il ciclo porta allo stesso errore che hai trovato. for i in list(d)emula il comportamento leggermente inefficiente di Python 2 di copiare le chiavi in ​​un elenco prima di iterare, per circostanze speciali come la tua.
— Michael Krebs,

la soluzione python3 non funziona quando si desidera eliminare un oggetto in dict interno. per esempio hai un dic A e un dict B in dict A. se vuoi eliminare un oggetto in dict B si verifica un errore
— Ali-T

1
In python3.x, list(d.keys())crea lo stesso output di list(d), chiamando lista dictrestituisce le chiavi. La keyschiamata (anche se non così costosa) non è necessaria.
— Sean Breckenridge

46

Basta usare la comprensione del dizionario per copiare gli elementi rilevanti in un nuovo dict

>>> d
{'a': [1], 'c': [], 'b': [1, 2], 'd': []}
>>> d = { k : v for k,v in d.iteritems() if v}
>>> d
{'a': [1], 'b': [1, 2]}

Per questo in Python 3

>>> d
{'a': [1], 'c': [], 'b': [1, 2], 'd': []}
>>> d = { k : v for k,v in d.items() if v}
>>> d
{'a': [1], 'b': [1, 2]}

11
d.iteritems()mi ha dato un errore. Ho usato d.items()invece - usando python3
— wcyn il

4
Questo funziona per il problema posto nella domanda di OP. Tuttavia, chiunque sia venuto qui dopo aver colpito questo RuntimeError in codice multi-thread, sii consapevole che il GIL di CPython può essere rilasciato anche nel mezzo della comprensione dell'elenco e devi risolverlo in modo diverso.
— Yirkha,

40

Devi solo usare "copia":

In questo modo puoi scorrere i campi del dizionario originale e al volo puoi cambiare il dict desiderato (d dict). Funziona su ogni versione di Python, quindi è più chiaro.

In [1]: d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}

In [2]: for i in d.copy():
   ...:     if not d[i]:
   ...:         d.pop(i)
   ...:         

In [3]: d
Out[3]: {'a': [1], 'b': [1, 2]}

Lavorato! Grazie.
— Guilherme Carvalho Lithg,

13

Vorrei provare a evitare di inserire liste vuote in primo luogo, ma, in genere, utilizzerei:

d = {k: v for k,v in d.iteritems() if v} # re-bind to non-empty

Se precedente alla 2.7:

d = dict( (k, v) for k,v in d.iteritems() if v )

o semplicemente:

empty_key_vals = list(k for k in k,v in d.iteritems() if v)
for k in empty_key_vals:
    del[k]

+1: l'ultima opzione è interessante perché copia solo le chiavi di quegli elementi che devono essere eliminati. Ciò può fornire prestazioni migliori se è necessario eliminare solo un numero limitato di elementi rispetto alla dimensione del dict.
— Mark Byers,

@MarkByers yup - e se un numero elevato lo fa, ricollegare il dict a uno nuovo filtrato è un'opzione migliore. È sempre l'aspettativa di come dovrebbe funzionare la struttura
— Jon Clements

4
Un pericolo con il rebinding è che se da qualche parte nel programma c'era un oggetto che conteneva un riferimento al vecchio dict non vedeva i cambiamenti. Se sei certo che non è così, allora certo ... è un approccio ragionevole, ma è importante capire che non è esattamente lo stesso che modificare il dict originale.
— Mark Byers,

@MarkByers punto estremamente positivo - Io e te lo sappiamo (e innumerevoli altri), ma non è ovvio per tutti. E metterò i soldi sul tavolo che non ti ha nemmeno morso nella parte posteriore :)
— Jon Clements

Il punto di evitare di inserire le voci vuote è molto buono.
— Magnus Bodin il

12

Per Python 3:

{k:v for k,v in d.items() if v}

Bello e conciso. Ha funzionato anche per me in Python 2.7.
— donrondadon,

6

Non è possibile scorrere un dizionario mentre cambia durante il ciclo for. Crea un casting per elencarlo e scorrere su quell'elenco, funziona per me.

    for key in list(d):
        if not d[key]: 
            d.pop(key)

1

Per situazioni come questa, mi piace fare una copia profonda e scorrere quella copia mentre modifica il dict originale.

Se il campo di ricerca è all'interno di un elenco, è possibile enumerare il ciclo for dell'elenco e quindi specificare la posizione come indice per accedere al campo nel dict originale.


1

Questo ha funzionato per me:

dict = {1: 'a', 2: '', 3: 'b', 4: '', 5: '', 6: 'c'}
for key, value in list(dict.items()):
    if (value == ''):
        del dict[key]
print(dict)
# dict = {1: 'a', 3: 'b', 6: 'c'}  

Il cast degli elementi del dizionario da elencare crea un elenco dei suoi elementi, in modo da poter iterare su di esso ed evitare il RuntimeError.


1

dictc = {"stName": "asas"} keys = dictc.keys () per la chiave nella lista (chiavi): dictc [key.upper ()] = 'Nuovo valore' stampa (str (dictc))


0

Il motivo dell'errore di runtime è che non è possibile eseguire l'iterazione attraverso una struttura di dati mentre la struttura cambia durante l'iterazione.

Un modo per ottenere ciò che stai cercando è utilizzare l'elenco per aggiungere le chiavi che desideri rimuovere e quindi utilizzare la funzione pop sul dizionario per rimuovere la chiave identificata durante l'iterazione dell'elenco.

d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}
pop_list = []

for i in d:
        if not d[i]:
                pop_list.append(i)

for x in pop_list:
        d.pop(x)
print (d)

0

Python 3 non consente l'eliminazione durante l'iterazione del dizionario (usando per il ciclo sopra). Ci sono varie alternative da fare; un modo semplice è quello di cambiare la seguente riga

for i in x.keys():

Con

for i in list(x)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.