TypeError: unhashable type: „dict”

175

Ten fragment kodu daje mi błąd, unhashable type: dictczy ktoś może mi wyjaśnić, jakie jest rozwiązanie

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
źródło
3
Byłoby użyteczne, aby zobaczyć raport o błędzie, dzięki czemu możemy zobaczyć, które linia ma problemu ...
drevicko

Odpowiedzi:

248

Próbujesz użyć a dictjako klucza do innego dictlub w set. To nie działa, ponieważ klucze muszą być hashowane. Zasadniczo tylko niezmienne obiekty (ciągi znaków, liczby całkowite, zmiennoprzecinkowe, zestawy zamrożone, krotki elementów niezmiennych) są hashowane (choć możliwe są wyjątki). Więc to nie działa:

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

Aby użyć dykta jako klucza, musisz przekształcić go w coś, co może być najpierw zaszyfrowane. Jeśli dykt, którego chcesz użyć jako klucza, składa się tylko z niezmiennych wartości, możesz utworzyć jego haszowalną reprezentację w następujący sposób:

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

Teraz możesz użyć keyjako klucza w a dictlub set:

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

Oczywiście musisz powtórzyć ćwiczenie, gdy chcesz coś sprawdzić za pomocą dykta:

>>> 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

Jeśli dictklucz, którego chcesz użyć jako klucza, ma wartości, które same są dyktami i / lub listami, musisz rekurencyjnie „zamrozić” przyszły klucz. Oto punkt wyjścia:

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
źródło
2
Dzięki, to działa, ale nadal pojawia się błąd, jeśli wartość jest dyktem lub listą (nie można haszować), teraz używam hasha (str (my_dict)), działa dobrze dla mnie.
Steven Du
7
tylko uwaga Słowniki @StevenDu nie gwarantują porządku, więc str(my_dict)mogą zwrócić dwa różne ciągi dla tego samego (lub innego, ale równoważnego) dyktu
K Raphael
1
Aby przekonwertować wynikowy zestaw zamrożenia z powrotem na dykt, po prostu zadzwoń dict(the_frozenset).
użytkownik
4
Wydaje mi się, że frozenset(dict_key.items())jest to potencjalnie problematyczne, ponieważ dwa dykty z tą samą zawartością, ale inna kolejność wstawiania mogą nie dawać tego samego klucza. Dodawanie wywołania do sort () wydaje się być w porządku. Np. frozenset(sorted(dict_key.items()))Frozenset wydaje się dziwnym wyborem, biorąc pod uwagę, że zestawy są wyraźnie nieuporządkowane. Prawdopodobnie działa dobrze w praktyce, ale krotka wydaje mi się bardziej logicznym wyborem. Poszedłem ztuple(sorted(dict_key.items()))
Jasonem Heissem
Zgadzam się z @JasonHeiss
user3732361
6

Możliwym rozwiązaniem może być użycie metody JSON dumps (), dzięki czemu można przekonwertować słownik na ciąg ---

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

Wynik -

set(['{"a": 10, "b": 20}'])
True
Matteo Boscolo
źródło
2
Tak powinno być dumps, nie dump.
Kushan Gunasekera