Reputation: 3919
This piece of code is giving me an error
TypeError: unhashable type: dict
Can anyone explain to me what the solution is?
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)
Upvotes: 387
Views: 987373
Reputation: 981
Used nested function to convert the dict at it nested dict to tuple.
def make_hashable(d: dict[str, Any]) -> tuple:
"""Convert a dictionary, which is un-hashable into a tuple which is a hashable form."""
return tuple(
sorted(
(k, make_hashable(v)) if isinstance(v, dict) else (k, v)
for k, v in d.items()
)
)
Upvotes: 0
Reputation: 425
To get a set of dicts (which you can't create since dict are mutable) I recommend you use instead a dict of dict. You can then combine that with @StevenDu's suggestion
mydict: Dict[int, dict] = {}
d1 = {'a': 1, 'b': 2}
mydict[hash(str(d1))] = d1
Please note that instead of 'hash(str(d1))' you could use any custom way to get a unique key for your dict.
Upvotes: 0
Reputation: 1474
There is a good package called frozendict
pip3 install frozendict
from frozendict import frozendict
And then either
d = frozendict(role="user", content=prompt)
or
d = frozendict(my_dict)
Upvotes: -1
Reputation: 23011
This error occurs if you try to use a dictionary as a key for another dictionary (e.g. {{}: 1}
) or add a dictionary to a set (e.g. {{}}
) or check if a dict exists in set/dict_keys (e.g. {} in set()
) or use a column of dicts as a grouper in pandas groupby.
Possible solutions:
dict.items()
-> tuple
If insertion order of the dicts is important (which is lost if converted to frozensets), then refactor your code to convert the dict(s) to be used as dict key/added into a set into a tuple. For the example, the problem in the OP was that there was an attempt to use a dictionary (returned from word_feats
function) as a key for another dictionary. For example,
# 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
So for the example in the OP, instead of returning a dict
, returning a tuple
solves the problem.
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
This solution is useful if you were trying to cache a dictionary returned from function using the @functools.lru_cache()
decorator and got this error. Refactoring the function to return a tuple instead solves the error.
dict
-> str
Another way is to simply convert the dictionary into a string. Similar to tuple()
, it preserves insertion order. Then if the stringified key needs to be converted back into a dict, ast.literal_eval()
from the standard library can be used to recover it.
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}]
dict.items()
-> frozenset
Because frozensets don't preserve order, it's ideal if you wanted to add dicts into a set, perhaps to find unique dicts in a list. Then to recover the original dictionary from the frozensets, call dict()
on each frozenset. For example,
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}]
As the output of the last line of code above shows, only one of lst[0]
and lst[1]
was correctly kept, since lst[0]==lst[1]
is True.
dict
-> json.dumps()
If the dicts are json serializable, then converting to json objects can be used to find unique dicts in a list too. If you want to make sure that the order of keys don't matter, use the sort_keys=
parameter of json.dumps()
. However, one important thing to note is that json requires the keys to be strings, so if the keys are numeric (as below), then converting to json and back into a dict may not recover the original dict if there are non-string keys.
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}]
Upvotes: 3
Reputation: 190
def frozendict(d: dict):
keys = sorted(d.keys())
return tuple((k, d[k]) for k in keys)
Implementing the function above returns an immutable structure, and we prefer sorting the key to prevent out-of-order iterations.
Upvotes: -1
Reputation: 1195
This happened to me because I was thinking in Typescript, and tried to set a python dictionary up like this:
thing = { 'key': 'value' }
other_thing = {'other_key': 'other_value'}
my_dictionary = { thing, other_thing }
Then I tried:
my_dictionary = { thing: thing, other_thing: other_thing }
...which still did not work
What ended up working was...
my_dictionary = { 'thing': thing, 'other_thing': other_thing }
Funny how used we get to the little syntax tricks from different languages...
Upvotes: 10
Reputation: 577
A possible solution might be to use the JSON dumps() method, so you can convert the dictionary to a string ---
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
Output -
set(['{"a": 10, "b": 20}'])
True
Upvotes: 43
Reputation: 50975
You're trying to use a dict
as a key to another dict
or in a set
. That does not work because the keys have to be hashable. As a general rule, only immutable objects (strings, integers, floats, frozensets, tuples of immutables) are hashable (though exceptions are possible). So this does not work:
>>> dict_key = {"a": "b"}
>>> some_dict[dict_key] = True
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'
To use a dict as a key you need to turn it into something that may be hashed first. If the dict you wish to use as key consists of only immutable values, you can create a hashable representation of it like this:
>>> key = frozenset(dict_key.items())
Now you may use key
as a key in a dict
or set
:
>>> some_dict[key] = True
>>> some_dict
{frozenset([('a', 'b')]): True}
Of course you need to repeat the exercise whenever you want to look up something using a 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
If the dict
you wish to use as key has values that are themselves dicts and/or lists, you need to recursively "freeze" the prospective key. Here's a starting point:
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
Upvotes: 489