TypeError: unhashable тип: 'dict'

175

Этот кусок кода дает мне ошибку, unhashable type: dictможет кто-нибудь объяснить мне, каково решение

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
источник
3
Было бы полезно показать отчет об ошибке, чтобы мы могли увидеть, в какой строке возникла проблема ...
drevicko

Ответы:

249

Вы пытаетесь использовать dictкак ключ к другому dictили в set. Это не работает, потому что ключи должны быть хэшируемыми. Как правило, только неизменяемые объекты (строки, целые числа, числа с плавающей запятой, заморозки, кортежи неизменяемых объектов) являются хэшируемыми (хотя возможны исключения). Так что это не работает:

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

Чтобы использовать диктовку в качестве ключа, нужно сначала превратить его в нечто, что может быть хешировано. Если диктовка, которую вы хотите использовать в качестве ключа, состоит только из неизменяемых значений, вы можете создать ее хешируемое представление следующим образом:

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

Теперь вы можете использовать keyв качестве ключа dictили set:

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

Конечно, вам нужно повторять упражнение всякий раз, когда вы хотите найти что-то, используя 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

Если dictвы хотите использовать в качестве ключа значения, которые сами по себе являются диктатами и / или списками, вам необходимо рекурсивно «заморозить» предполагаемый ключ. Вот отправная точка:

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
Лауриц В. Таулов
источник
2
Спасибо, это работает, однако все равно получаю ошибку, если значение является dict или list (unhashable), теперь я использую hash (str (my_dict)), работает нормально для меня.
Стивен Дю
7
просто примечание Словари @StevenDu не гарантируют порядок, поэтому str(my_dict)могут возвращать две разные строки для одинаковых (или разных, но эквивалентных) слов
K Raphael
1
Чтобы преобразовать полученный Frozenset обратно в dict, просто позвоните dict(the_frozenset).
пользователь
4
Мне кажется, что frozenset(dict_key.items())это потенциально проблематично, потому что два текста с одинаковым содержимым, но с разным порядком вставки, могут не приводить к одному и тому же ключу. Добавление вызова к sorted () выглядит по порядку. Например frozenset(sorted(dict_key.items())), frozenset кажется странным выбором, поскольку множества явно неупорядочены. Возможно, на практике это работает нормально, но мне кажется, что кортеж более логичен. Я пошел сtuple(sorted(dict_key.items()))
Джейсон Хейс
Согласен с @JasonHeiss
user3732361
6

Возможным решением может быть использование метода JSON dumps (), чтобы вы могли преобразовать словарь в строку ---

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

Вывод -

set(['{"a": 10, "b": 20}'])
True
Маттео Босколо
источник
2
Должно быть dumps, нет dump.
Кушан Гунасекера