Come si inizializza un dizionario di elenchi vuoti in Python?


88

Il mio tentativo di creare a livello di codice un dizionario di elenchi non riesce a consentirmi di indirizzare individualmente le chiavi del dizionario. Ogni volta che creo il dizionario degli elenchi e provo ad aggiungerlo a una chiave, vengono aggiornati tutti. Ecco un test case molto semplice:

data = {}
data = data.fromkeys(range(2),[])
data[1].append('hello')
print data

Risultato attuale: {0: ['hello'], 1: ['hello']}

Risultato atteso: {0: [], 1: ['hello']}

Ecco cosa funziona

data = {0:[],1:[]}
data[1].append('hello')
print data

Risultato effettivo e previsto: {0: [], 1: ['hello']}

Perché il fromkeysmetodo non funziona come previsto?

Risposte:


111

Passare []come secondo argomento a dict.fromkeys()fornisce un risultato piuttosto inutile: tutti i valori nel dizionario saranno lo stesso oggetto elenco.

In Python 2.7 o versioni successive, puoi invece utilizzare una comprensione dicitonica:

data = {k: [] for k in range(2)}

Nelle versioni precedenti di Python, puoi usare

data = dict((k, []) for k in range(2))

3
Bene, questo è un comportamento piuttosto non intuitivo, qualche idea sul perché lo stesso oggetto viene utilizzato per tutte le chiavi?
Bar

2
@Bar Perché non c'è nient'altro che la funzione possa fare all'interno della semantica del linguaggio Python. Si passa un singolo oggetto da utilizzare come valore per tutte le chiavi, in modo che il singolo oggetto venga utilizzato per tutte le chiavi. Sarebbe meglio che il fromkeys()metodo accettasse invece una funzione di fabbrica, quindi potremmo passare listcome funzione, e quella funzione verrebbe chiamata una volta per ogni chiave creata, ma non è l'API effettiva di dict.fromkeys().
Sven Marnach

3
Questo non è affatto intuitivo. Mi ci è voluta un'ora per trovarlo. Grazie
Astrid

1
La stessa cosa accade se passi dict () come secondo argomento. Comportamento molto sconcertante.
Orly,

@Orly Questo perché prima viene creato un dizionario vuoto, quindi viene passato un riferimento a esso a tutte le inizializzazioni.
Dr_Zaszuś

83

Usa invece defaultdict :

from collections import defaultdict
data = defaultdict(list)
data[1].append('hello')

In questo modo non è necessario inizializzare in anticipo tutte le chiavi che si desidera utilizzare negli elenchi.

Quello che sta accadendo nel tuo esempio è che usi un elenco (modificabile):

alist = [1]
data = dict.fromkeys(range(2), alist)
alist.append(2)
print data

produrrebbe {0: [1, 2], 1: [1, 2]}.


2
Nel mio caso, ho bisogno di inizializzare tutte le chiavi in ​​anticipo in modo che il resto della logica del programma possa funzionare come previsto, ma altrimenti questa sarebbe una buona soluzione. Grazie.
Martin Burch

Immagino che ciò che manca da questa risposta sia dire che questa soluzione funziona, al contrario di quella dell'OP, perché listqui non è un elenco vuoto, ma un tipo (o puoi vederlo come un costruttore richiamabile, immagino). Quindi, ogni volta che viene passata una chiave mancante, viene creato un nuovo elenco invece di riutilizzare lo stesso.
Dr_Zaszuś

8

Stai popolando i tuoi dizionari con riferimenti a un singolo elenco, quindi quando lo aggiorni, l'aggiornamento si riflette su tutti i riferimenti. Prova invece una comprensione del dizionario. Vedi Creare un dizionario con comprensione di elenchi in Python

d = {k : v for k in blah blah blah}

ottimo suggerimento sull'inizializzazione dei valori del dizionario ... grazie cobie! Ho esteso il tuo esempio per ripristinare i valori in un dizionario esistente, d. Ho eseguito questo come segue: d = {k: 0 for k in d}
John

Cosa c'è vin questa risposta?
Dr_Zaszuś

-2

Potresti usare questo:

data[:1] = ['hello']

2
Potrebbe essere utile all'OP spiegare perché funziona. La domanda originale ha pubblicato aks perché non funziona come previsto.
william.taylor.09

@ william.taylor.09 è abbastanza ovvio perché funziona, non è vero?
Conner Dassen

OP sta chiedendo "Perché il metodo fromkeys non funziona come previsto?"
william.taylor.09
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.