TypeError: type non détachable: 'dict'

175

Ce morceau de code me donne une erreur, unhashable type: dictquelqu'un peut-il m'expliquer quelle est la solution

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)
user1805250
la source
3
Il serait utile d'afficher le rapport d'erreur afin que nous puissions voir quelle ligne a le problème ...
drevicko

Réponses:

249

Vous essayez d'utiliser a dictcomme clé d'un autre dictou dans un fichier set. Cela ne fonctionne pas car les clés doivent être hachables. En règle générale, seuls les objets immuables (chaînes, entiers, flottants, frozensets, tuples d'immuables) sont hachables (bien que des exceptions soient possibles). Donc cela ne fonctionne pas:

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

Pour utiliser un dict comme clé, vous devez d'abord le transformer en quelque chose qui peut être haché. Si le dict que vous souhaitez utiliser comme clé se compose uniquement de valeurs immuables, vous pouvez en créer une représentation hachable comme ceci:

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

Vous pouvez maintenant utiliser keycomme clé dans un dictou set:

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

Bien sûr, vous devez répéter l'exercice chaque fois que vous souhaitez rechercher quelque chose à l'aide d'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 la dictclé que vous souhaitez utiliser a des valeurs qui sont elles-mêmes des dictionnaires et / ou des listes, vous devez "figer" récursivement la clé prospective. Voici un point de départ:

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
la source
2
Merci, cela fonctionne, mais toujours une erreur si la valeur est un dict ou une liste (unhashable), maintenant j'utilise hash (str (my_dict)), fonctionne bien pour moi.
Steven Du
7
juste une note Les dictionnaires @StevenDu ne garantissent pas l'ordre, ils str(my_dict)pourraient donc renvoyer deux chaînes différentes pour les mêmes dictionnaires (ou différents, mais équivalents)
K Raphael
1
Pour reconvertir le frozenset résultant en dict, appelez simplement dict(the_frozenset).
utilisateur
4
Il me semble que frozenset(dict_key.items())c'est potentiellement problématique en ce que deux dictionnaires avec le même contenu mais un ordre d'insertion différent peuvent ne pas donner la même clé. L'ajout d'un appel à sorted () semble dans l'ordre. Par exemple frozenset(sorted(dict_key.items())), frozenset semble être un choix étrange étant donné que les ensembles sont explicitement non ordonnés. Cela fonctionne probablement bien dans la pratique, mais le tuple me semble être un choix plus logique. Je suis allé avectuple(sorted(dict_key.items()))
Jason Heiss
D'accord avec @JasonHeiss
user3732361
6

Une solution possible pourrait être d'utiliser la méthode JSON dumps (), afin de pouvoir convertir le dictionnaire en une chaîne ---

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

Production -

set(['{"a": 10, "b": 20}'])
True
Matteo Boscolo
la source
2
Ça devrait être dumps, non dump.
Kushan Gunasekera