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_feats
function) как ключ для другого словаря. Например,
# 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())