TypeError: unhashable type: '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)


3
Було б корисно показати звіт про помилку, щоб ми могли побачити, у якому рядку є проблема ...
drevicko

Відповіді:


248

Ви намагаєтесь використовувати 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}

Звичайно, вам потрібно повторювати вправу, коли ви хочете щось шукати за допомогою дикта:

>>> 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
Дякую, він працює, однак все одно отримую помилку, якщо значення є диктом або списком (незмінним), тепер я використовую хеш (str (мій вирок)), добре працює для мене.
Стівен Ду

7
просто примітка Словники @StevenDu не гарантують порядок, тому str(my_dict)можуть повернути два різні рядки для однакових (або різних, але еквівалентних) диктів
K Raphael

1
Щоб перетворити отриманий заморожений набір назад у дік, просто зателефонуйте dict(the_frozenset).
користувач

4
Мені здається, frozenset(dict_key.items())це потенційно проблематично в тому, що два дикти з однаковим вмістом, але різним порядком вставки можуть не призвести до одного ключа. Додавання дзвінка до сортованого () здається в порядку. Наприклад frozenset(sorted(dict_key.items())), заморожений набір здається дивним вибором, враховуючи, що набори явно не упорядковані. Це, мабуть, добре працює на практиці, але кортеж здається мені більш логічним вибором. Я пішовtuple(sorted(dict_key.items()))
Джейсон

Погодьтеся з @JasonHeiss
користувачем3732361

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.
Кушань Гунасекера
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.