TypeError: unhashable тип: 'dict'

Этот кусок кода дает мне ошибку 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)

5 ответов

Вы пытаетесь использовать 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

Возможным решением может быть использование метода 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

Это случилось со мной, потому что я думал в Typescript и пытался настроить словарь Python следующим образом:

      thing = { 'key': 'value' }
other_thing = {'other_key': 'other_value'}
my_dictionary = { thing, other_thing }

Затем я попробовал:

      my_dictionary = { thing: thing, other_thing: other_thing }

... который все еще не работал

Что в итоге сработало...

      my_dictionary = { 'thing': thing, 'other_thing': other_thing }

Забавно, как мы привыкли к маленьким синтаксическим трюкам из разных языков...

Эта ошибка возникает, если вы пытаетесь использовать словарь в качестве ключа для другого словаря (например,{{}: 1}) или добавить словарь в набор (например,{{}}) или проверьте, существует ли dict в set/dict_keys (например,{} in set()).

Возможные решения:

1. ->

Если важен порядок вставки диктовок (который теряется при преобразовании в замороженные наборы), реорганизуйте свой код, чтобы преобразовать диктовку(ы), которые будут использоваться в качестве ключа диктовки/добавлены в набор в кортеж. Например, проблема в ОП заключалась в том, что была попытка использовать словарь (возвращенный изword_featsfunction) как ключ для другого словаря. Например,

      # dict as key of another dict
d1 = {'a': 1, 'b': 2}
d2 = {d1: 3}                                # <--- TypeError: unhashable type: 'dict'
d2 = {tuple(d1.items()): 3}                 # <--- OK

# dicts in a set
st = {d1, d2}                               # <--- TypeError: unhashable type: 'dict'
st = {tuple(x.items()) for x in (d1, d2)}   # <--- OK

# membership tests
d1 in d2                                    # <--- TypeError: unhashable type: 'dict'
tuple(d1.items()) in d2                     # True

Итак, для примера в ОП вместо возврата a возвращается atupleрешает проблему.

      def word_feats(words):
    return dict([(word, True) for word in words])     # <--- TypeError

def word_feats(words):
    return tuple((word, True) for word in words)      # <--- OK

Это решение полезно, если вы пытались кэшировать словарь, возвращаемый функцией, с помощью@functools.lru_cache()декоратор и получил эту ошибку. Рефакторинг функции для возврата кортежа решает проблему.

2. ->str

Другой способ — просто преобразовать словарь в строку. Похожий наtuple(), он сохраняет порядок вставки. Затем, если строковый ключ необходимо преобразовать обратно в dict,ast.literal_eval()из стандартной библиотеки можно использовать для его восстановления.

      import ast

d1 = {'a': 1, 'b': 2}
d2 = {str(d1): 3}                               # {"{'a': 1, 'b': 2}": 3}

str(d1) in d2                                   # True

[ast.literal_eval(key) for key in d2.keys()]    # [{'a': 1, 'b': 2}]

3.dict.items()->frozenset

Поскольку замороженные наборы не сохраняют порядок, это идеально, если вы хотите добавить словари в набор, например, чтобы найти уникальные словари в списке. Затем, чтобы восстановить исходный словарь из замороженных наборов, вызовитеdict()на каждом замороженном наборе. Например,

      lst = [{1:3, 2:0}, {2:0, 1:3}, {2:3}]      # list of dicts
set(lst)                                   # <--- TypeError: unhashable type: 'dict'

st = {frozenset(d.items()) for d in lst}   # convert each dict into a frozenset
# convert back into a list of unique dicts
[dict(d) for d in st]                      # [{2: 3}, {2: 0, 1: 3}]

Как показывает вывод последней строки кода выше, только один изlst[0]иlst[1]сохранялось правильно, посколькуlst[0]==lst[1]правда.

4.dict->

Если словари являются сериализуемыми в формате json, то преобразование в объекты json можно использовать и для поиска уникальных слов в списке. Если вы хотите убедиться, что порядок ключей не имеет значения, используйтеsort_keys=параметрjson.dumps(). Однако следует отметить одну важную вещь: json требует, чтобы ключи были строками, поэтому, если ключи являются числовыми (как показано ниже), преобразование в json и обратно в dict может не восстановить исходный dict, если есть нестроковые ключи..

      import json
lst1 = [{1:3, 2:0}, {2:0, 1:3}]
[json.loads(j) for j in {json.dumps(d, sort_keys=True) for d in lst1}] 
# [{'1': 3, '2': 0}]
      def frozendict(d: dict):
    return frozenset(d.keys()), frozenset(d.values())
Другие вопросы по тегам