2011-08-31 19 views
61

Sono un po 'confuso su cosa può/non può essere usato come chiave per un ditt di python.Perché non posso usare una lista come una chiave dict in python?

dicked = {} 
dicked[None] = 'foo'  # None ok 
dicked[(1,3)] = 'baz' # tuple ok 
import sys 
dicked[sys] = 'bar'  # wow, even a module is ok ! 
dicked[(1,[3])] = 'qux' # oops, not allowed 

Quindi un tupla è un tipo immutabile, ma se mi nascondo un elenco all'interno di esso, allora non può essere una chiave .. Non potrei altrettanto facilmente nascondere un elenco all'interno di un modulo?

Ho avuto una vaga idea che la chiave deve essere "lavabile", ma devo ammettere la mia ignoranza sui dettagli tecnici; Non so cosa sta succedendo qui. Cosa sarebbe andato storto se si tentasse di usare gli elenchi come chiavi, con l'hash come, ad esempio, la loro posizione di memoria?

+1

Ecco una buona discussione: http://stackoverflow.com/questions/2671211/create-a-dictionary-in-python-which-is-indexed-by-lists – Hernan

+25

Hai una risatina fuori il tuo nome variabile. – kindall

risposta

21

C'è un buon articolo sull'argomento nel wiki di Python: Why Lists Can't Be Dictionary Keys. Come spiegato qui:

Cosa sarebbe andato storto se si tentasse di utilizzare gli elenchi come chiavi, con l'hash come, ad esempio, la loro posizione di memoria?

Si può fare senza veramente infrangere nessuno dei requisiti, ma porta a comportamenti imprevisti. Gli elenchi vengono generalmente trattati come se il loro valore fosse derivato dai valori del loro contenuto, ad esempio quando si verifica l'uguaglianza (in). Molti - comprensibilmente - si aspettano che sia possibile utilizzare qualsiasi elenco [1, 2] per ottenere la stessa chiave, in cui si dovrebbe mantenere esattamente lo stesso oggetto elenco. Ma la ricerca per interruzioni di valore non appena viene modificato un elenco usato come chiave, e per la ricerca per identità, richiede di mantenere esattamente lo stesso elenco, che non è richiesto per nessun'altra operazione di elenco comune (almeno nessuno che io possa pensare).

Altri oggetti come i moduli e object fanno comunque un affare molto più grande della loro identità oggetto (quando era l'ultima volta che si avevano due oggetti modulo distinti chiamati sys?), E vengono comunque confrontati.Pertanto, è meno sorprendente - o addirittura previsto - che, se usati come chiavi dict, si confrontino per identità anche in quel caso.

7

Ecco una risposta http://wiki.python.org/moin/DictionaryKeys

Che cosa sarebbe andare male se si è tentato di utilizzare gli elenchi come chiavi, con l'hash come, ad esempio, la loro posizione di memoria?

Cercare elenchi diversi con lo stesso contenuto produce risultati diversi, anche se il confronto di elenchi con lo stesso contenuto li indicherebbe come equivalenti.

E a proposito di utilizzare un elenco letterale in una ricerca di dizionario?

9

Il problema è che le tuple sono immutabili e gli elenchi non lo sono. Considerare il seguente

d = {} 
li = [1,2,3] 
d[li] = 5 
li.append(4) 

Cosa deve restituire d[li]? È la stessa lista? Che ne pensi di d[[1,2,3]]? Ha gli stessi valori, ma è un elenco diverso?

In definitiva, non esiste una risposta soddisfacente. Ad esempio, se l'unica chiave che funziona è la chiave originale, se non si ha alcun riferimento a quella chiave, non sarà più possibile accedere nuovamente al valore. Con ogni altra chiave consentita, puoi costruire una chiave senza un riferimento all'originale.

Se entrambi i miei suggerimenti funzionano, allora avete chiavi molto diverse che restituiscono lo stesso valore, il che è più che un po 'sorprendente. Se solo il contenuto originale funziona, allora la tua chiave andrà rapidamente male, dal momento che le liste sono fatte per essere modificate.

+0

Sì, è la stessa lista quindi mi aspetto che 'd [li]' rimanga 5. 'd [[1,2,3]]' farebbe riferimento ad un oggetto lista diverso come chiave, quindi sarebbe un KeyError . Non vedo ancora alcun problema ... tranne il fatto che lasciare che una chiave raccolga i rifiuti potrebbe rendere inaccessibili alcuni dei valori di dett. Ma questo è un problema pratico, non un problema logico. – wim

+0

@wim: 'd [list (li)]' essere un KeyError fa parte del problema. In quasi * ogni altro use case *, 'li' non è distinguibile da una nuova lista con contenuti identici. Funziona, ma è contro-intuitivo per molti. Inoltre, quando è stata l'ultima volta che * davvero * devi usare una lista come tasto di dettatura? L'unico caso d'uso che posso immaginare è quando si sta tagliando tutto per identità in ogni caso, e in tal caso si dovrebbe semplicemente farlo invece di fare affidamento su '__hash__' e' __eq__' per essere basati sull'identità. – delnan

+0

@delnan È il _problem_ semplicemente che non sarebbe molto utile dettato da tali complicazioni? o c'è qualche ragione per cui potrebbe effettivamente infrangere un dittico? – wim

2

tuo awnser può essere trovato qui:

Perché liste non possono essere Dizionario chiavi

I nuovi arrivati ​​a Python spesso si chiedono perché, mentre la lingua comprende sia una tupla e un tipo di elenco, tuple sono utilizzabili come tasti di dizionario, mentre gli elenchi non lo sono. Questa è stata una decisione deliberata di progettazione e può essere meglio spiegata da spiegando innanzitutto come funzionano i dizionari Python.

Fonte & maggiori informazioni: http://wiki.python.org/moin/DictionaryKeys

0

Secondo la documentazione Python 2.7.2:

Un oggetto è hashable se ha un valore hash che non cambia mai durante la sua vita (è ha bisogno di un metodo di hash() e può essere rispetto ad altri oggetti (necessita di un eq() o cmp Metodo()). Gli oggetti che possono essere confrontati uguali devono avere lo stesso valore di hash.

La facilità di utilizzo rende un oggetto utilizzabile come chiave di dizionario e un membro , poiché queste strutture dati utilizzano internamente il valore hash.

Tutti gli oggetti incorporati immutabili di Python sono lavabili, mentre non esistono contenitori modificabili (ad esempio elenchi o dizionari). Gli oggetti che sono istanze di classi definite dall'utente sono disponibili per impostazione predefinita; loro tutti si confrontano in modo non uguale e il loro valore di hash è il loro id().

Una tupla è immutabile nel senso che non è possibile aggiungere, rimuovere o sostituire i suoi elementi, ma gli elementi stessi possono essere modificabili. Il valore hash dell'elenco dipende dai valori hash dei suoi elementi e quindi cambia quando si modificano gli elementi.

L'utilizzo di id per gli hash delle liste implicherebbe che tutte le liste si confrontino in modo diverso, il che sarebbe sorprendente e scomodo.

+0

Questo non risponde alla domanda, vero? 'hash = id' non infrange l'invariante alla fine del primo paragrafo, la domanda è perché non è fatto in quel modo. – delnan

+0

@delnan: ho aggiunto l'ultimo paragrafo per chiarire. –

0

La semplice risposta alla tua domanda è che l'elenco classi non implementa il metodo hash che è richiesto per qualsiasi oggetto che desideri utilizzare come chiave in un dizionario. Tuttavia, il motivo per cui l'hash non è implementato nello stesso modo in cui si dice che la classe di tuple (basata sul contenuto del contenitore) è perché una lista è mutabile, pertanto la modifica dell'elenco richiederebbe ricalcolare l'hash che potrebbe significare elenca ora nel secchio sbagliato nella tabella hash sottostante. Si noti che poiché non è possibile modificare una tupla (immutabile), non si imbatte in questo problema.

Come nota a margine, l'effettiva implementazione della ricerca dictobjects si basa sull'Algoritmo D di Knuth Vol. 3, Sez. 6.4. Se hai quel libro a tua disposizione, potrebbe essere una lettura utile, inoltre se sei davvero interessato a dare un'occhiata ai commenti degli sviluppatori sull'effettivo implementation of dictobject here., entra nei dettagli di come funziona esattamente.C'è anche un python lecture sull'implementazione dei dizionari a cui potresti essere interessato. Passano attraverso la definizione di una chiave e cos'è un hash nei primi minuti.

15

Perché non posso usare un elenco come tasto dict in python?

>>> d = {repr([1,2,3]): 'value'} 
{'[1, 2, 3]': 'value'} 

(per chi inciampa su questa questione alla ricerca di un modo intorno ad esso)

come spiegato da altri qui, in effetti, non si può. Puoi comunque usare la sua rappresentazione di stringhe se vuoi davvero usare la tua lista.

+3

Scusa, non vedo davvero il tuo punto. Non è diverso dall'uso di stringhe letterali come chiavi. – wim

+3

Vero; Ho appena visto tante risposte in realtà spiegare perché non è possibile utilizzare le liste in termini di 'chiave deve essere lavabile', che è così vero, che volevo suggerire un modo per aggirarlo, nel caso qualcuno (nuovo) lo cercasse ... – Remi