TypeError: tipo no compartible: 'dict'

175

Este código me está dando un error, unhashable type: dict¿alguien puede explicarme cuál es la solución?

negids = movie_reviews.fileids('neg')
def word_feats(words):
    return dict([(word, True) for word in words])

negfeats = [(word_feats(movie_reviews.words(fileids=[f])), 'neg') for f in negids]
stopset = set(stopwords.words('english'))

def stopword_filtered_word_feats(words):
    return dict([(word, True) for word in words if word not in stopset])

result=stopword_filtered_word_feats(negfeats)
usuario1805250
fuente
3
Sería útil mostrar el informe de error para que podamos ver qué línea tiene el problema ...
drevicko

Respuestas:

248

Estás intentando usar a dictcomo clave para otro dicto en a set. Eso no funciona porque las claves tienen que ser hashaable. Como regla general, solo los objetos inmutables (cadenas, enteros, flotadores, congeladores, tuplas de inmutables) son hashables (aunque son posibles excepciones). Entonces esto no funciona:

>>> dict_key = {"a": "b"}
>>> some_dict[dict_key] = True
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'

Para usar un dict como clave, debe convertirlo en algo que se pueda descifrar primero. Si el dict que desea utilizar como clave consta solo de valores inmutables, puede crear una representación hashaable de esta manera:

>>> key = frozenset(dict_key.items())

Ahora puede usar keycomo clave en un dicto set:

>>> some_dict[key] = True
>>> some_dict
{frozenset([('a', 'b')]): True}

Por supuesto, debe repetir el ejercicio cada vez que desee buscar algo con un dict:

>>> some_dict[dict_key]                     # Doesn't work
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'
>>> some_dict[frozenset(dict_key.items())]  # Works
True

Si el dictque desea utilizar como clave tiene valores que son dictados y / o listas, debe "congelar" recursivamente la clave prospectiva. Aquí hay un punto de partida:

def freeze(d):
    if isinstance(d, dict):
        return frozenset((key, freeze(value)) for key, value in d.items())
    elif isinstance(d, list):
        return tuple(freeze(value) for value in d)
    return d
Lauritz V. Thaulow
fuente
2
Gracias, funciona, sin embargo, aún obtengo un error si el valor es un dict o una lista (no compartible), ahora estoy usando hash (str (my_dict)), funciona bien para mí.
Steven Du
77
solo una nota @StevenDu los diccionarios no garantizan el orden, por lo que str(my_dict)podrían devolver dos cadenas diferentes para los mismos dictados (o diferentes, pero equivalentes)
K Raphael
1
Para convertir el conjunto congelado resultante de nuevo a dict, simplemente llame dict(the_frozenset).
usuario
44
Me parece que frozenset(dict_key.items())es potencialmente problemático ya que dos dictados con el mismo contenido pero diferente orden de inserción pueden no dar como resultado la misma clave. Agregar una llamada a sorted () parece estar en orden. Por ejemplo frozenset(sorted(dict_key.items())), además, el conjunto congelado parece una elección extraña dado que los conjuntos están explícitamente desordenados. Probablemente funciona bien en la práctica, pero la tupla me parece una opción más lógica. Fui contuple(sorted(dict_key.items()))
Jason Heiss
De acuerdo con @JasonHeiss
usuario3732361
6

Una posible solución podría ser utilizar el método JSON dumps (), para que pueda convertir el diccionario en una cadena ---

import json

a={"a":10, "b":20}
b={"b":20, "a":10}
c = [json.dumps(a), json.dumps(b)]


set(c)
json.dumps(a) in c

Salida -

set(['{"a": 10, "b": 20}'])
True
Matteo Boscolo
fuente
2
Debería ser dumps, no dump.
Kushan Gunasekera