Python: TypeError: unhashable type: 'list'


94

Sto cercando di prendere un file simile a questo

AAA x 111
AAB x 111
AAA x 112
AAC x 123
...

E usa un dizionario per in modo che l'output assomigli a questo

{AAA: ['111', '112'], AAB: ['111'], AAC: [123], ...}

Questo è quello che ho provato

file = open("filename.txt", "r") 
readline = file.readline().rstrip()
while readline!= "":
    list = []
    list = readline.split(" ")
    j = list.index("x")
    k = list[0:j]
    v = list[j + 1:]
    d = {}
    if k not in d == False:
        d[k] = []
    d[k].append(v)
    readline = file.readline().rstrip()

Continuo a ricevere un TypeError: unhashable type: 'list'. So che le chiavi in ​​un dizionario non possono essere elenchi, ma sto cercando di inserire il mio valore in un elenco e non nella chiave. Mi chiedo se ho fatto un errore da qualche parte.

Risposte:


56

Come indicato dalle altre risposte, l'errore è dovuto a k = list[0:j]dove la tua chiave viene convertita in una lista. Una cosa che potresti provare è rielaborare il tuo codice per sfruttare la splitfunzione:

# Using with ensures that the file is properly closed when you're done
with open('filename.txt', 'rb') as f:
  d = {}
  # Here we use readlines() to split the file into a list where each element is a line
  for line in f.readlines():
    # Now we split the file on `x`, since the part before the x will be
    # the key and the part after the value
    line = line.split('x')
    # Take the line parts and strip out the spaces, assigning them to the variables
    # Once you get a bit more comfortable, this works as well:
    # key, value = [x.strip() for x in line] 
    key = line[0].strip()
    value = line[1].strip()
    # Now we check if the dictionary contains the key; if so, append the new value,
    # and if not, make a new list that contains the current value
    # (For future reference, this is a great place for a defaultdict :)
    if key in d:
      d[key].append(value)
    else:
      d[key] = [value]

print d
# {'AAA': ['111', '112'], 'AAC': ['123'], 'AAB': ['111']}

Nota che se stai usando Python 3.x, dovrai fare una piccola modifica per farlo funzionare correttamente. Se apri il file con rb, dovrai usare line = line.split(b'x')(il che ti assicura di dividere il byte con il tipo di stringa corretto). Puoi anche aprire il file usando with open('filename.txt', 'rU') as f:(o anche with open('filename.txt', 'r') as f:) e dovrebbe funzionare bene.


Ho provato questo e ottengo TypeError: il tipo str non supporta l'API del buffer sulla riga "line = line.split ('x')"
Keenan

1
@ user1871081 Ah, stai usando Python 3.x? Pubblicherò un aggiornamento che dovrebbe funzionare con quello.
RocketDonkey

31

Nota: questa risposta non risponde esplicitamente alla domanda posta. le altre risposte lo fanno. Poiché la domanda è specifica per uno scenario e l' eccezione sollevata è generale , questa risposta punta al caso generale.

I valori hash sono solo numeri interi utilizzati per confrontare rapidamente le chiavi del dizionario durante una ricerca nel dizionario.

Internamente, il hash()metodo chiama il __hash__()metodo di un oggetto che è impostato di default per qualsiasi oggetto.

Conversione di un elenco nidificato in un set

>>> a = [1,2,3,4,[5,6,7],8,9]
>>> set(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

Ciò accade a causa dell'elenco all'interno di un elenco che è un elenco che non può essere sottoposto a hashing. Che può essere risolto convertendo gli elenchi annidati interni in una tupla ,

>>> set([1, 2, 3, 4, (5, 6, 7), 8, 9])
set([1, 2, 3, 4, 8, 9, (5, 6, 7)])

Hashing esplicito di un elenco annidato

>>> hash([1, 2, 3, [4, 5,], 6, 7])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'


>>> hash(tuple([1, 2, 3, [4, 5,], 6, 7]))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

>>> hash(tuple([1, 2, 3, tuple([4, 5,]), 6, 7]))
-7943504827826258506

La soluzione per evitare questo errore è ristrutturare l'elenco per avere tuple annidate invece di elenchi.


4
cosa succede se l'elenco è troppo grande ?? sembra che sia una buona soluzione ma non abbastanza generale
msh855

1
@ msh855 c'è qualche limite di dimensione? Ho testato il dizionario con una tupla di dimensione 100.000 e per me ha funzionato bene (sto usando python 3.6)
Sreram

18

Stai cercando di usare k(che è un elenco) come chiave per d. Gli elenchi sono modificabili e non possono essere utilizzati come chiavi di comando.

Inoltre, non stai mai inizializzando gli elenchi nel dizionario, a causa di questa riga:

if k not in d == False:

Quale dovrebbe essere:

if k not in d == True:

Quale dovrebbe essere effettivamente:

if k not in d:

5

Il motivo per cui ricevi l' unhashable type: 'list'eccezione è perché k = list[0:j]imposta kcome una "fetta" dell'elenco, che è logicamente un altro elenco, spesso più breve. Quello che ti serve è ottenere solo il primo elemento nell'elenco, scritto in questo modo k = list[0]. Lo stesso per il v = list[j + 1:]quale dovrebbe essere solo v = list[2]per il terzo elemento della lista restituita dalla chiamata a readline.split(" ").

Ho notato molti altri probabili problemi con il codice, di cui ne menzionerò alcuni. Un grosso problema è che non vuoi (ri) inizializzare dcon d = {}per ogni riga letta nel ciclo. Un altro è che generalmente non è una buona idea nominare le variabili come qualsiasi tipo predefinito perché ti impedirà di accedere a uno di essi se ne hai bisogno e crea confusione per gli altri che sono abituati al nomi che designano uno di questi elementi standard. Per questo motivo, dovresti rinominare la variabile listvariabile in modo diverso per evitare problemi del genere.

Ecco una tua versione funzionante con queste modifiche, ho anche semplificato l' ifespressione dell'istruzione che hai che controlla per vedere se la chiave è già nel dizionario - ci sono modi impliciti ancora più brevi per fare questo genere di cose, ma usando un condizionale l'affermazione va bene per ora.

d = {}
file = open("filename.txt", "r")
readline = file.readline().rstrip()
while readline:
    lst = readline.split(" ") # Split into sequence like ['AAA', 'x', '111'].
    k = lst[0]  # First item.
    v = lst[2]  # Third item.
    if k not in d:  # New key?
        d[k] = []  # Initialize its associated value to an empty list.
    d[k].append(v)
    readline = file.readline().rstrip()

file.close()  # Done reading file.
print('d: {}'.format(d))

Produzione:

d: {'AAA': ['111', '112'], 'AAC': ['123'], 'AAB': ['111']}

0

La TypeErrorsta accadendo perché kè un elenco, dal momento che viene creato utilizzando una fetta da un altro elenco con la linea k = list[0:j]. Probabilmente dovrebbe essere qualcosa di simile k = ' '.join(list[0:j]), quindi hai invece una stringa.

Oltre a questo, la tua ifaffermazione non è corretta come notato dalla risposta di Jesse, che dovrebbe leggere if k not in do if not k in d(preferisco quest'ultima).

Stai anche cancellando il tuo dizionario ad ogni iterazione poiché hai d = {}dentro il tuo forciclo.

Nota che non dovresti nemmeno usare listo filecome nomi di variabili, poiché maschererai i incorporati.

Ecco come riscriverei il tuo codice:

d = {}
with open("filename.txt", "r") as input_file:
    for line in input_file:
        fields = line.split()
        j = fields.index("x")
        k = " ".join(fields[:j])
        d.setdefault(k, []).append(" ".join(fields[j+1:]))

Il dict.setdefault()metodo precedente sostituisce la if k not in dlogica del codice.


mentre la preferenza è un tuo pieno diritto, not k in dpotrebbe confondere un principiante in quanto (not k) in d, pur k not in dnon avendo ambiguità
Jesse the Game

Direi anche che è il modo "pitonico" not inindicato come operatore .
Jesse the Game

Sì, penso che la mia preferenza derivi probabilmente dall'imparare prima altre lingue, dove per qualcosa come un test di contenimento non avresti operatori per questo, quindi faresti qualcosa di simile !a.contains(b). not inpuò essere più pitonico, trovo solo il concetto di due operatori di parole più confuso rispetto all'utilizzo di un inverso su un'espressione booleana.
Andrew Clark

-1
    python 3.2

    with open("d://test.txt") as f:
              k=(((i.split("\n"))[0].rstrip()).split() for i in f.readlines())
              d={}
              for i,_,v in k:
                      d.setdefault(i,[]).append(v)
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.