Reputation: 24133
If an object is unhashable, like a set, it is an error to ask whether it's a key of a dictionary:
>>> key = set()
>>> key in {}
Traceback (most recent call last):
TypeError: unhashable type: 'set'
I'm just wondering why this is.
edit: I understand that the object needs to be hashed, but not why it needs to be an error if it can't.
Upvotes: 1
Views: 305
Reputation: 280564
It's more useful that way.
key in d
is supposed to be equivalent to
any(key == k for k in d)
which can actually be True for unhashable objects:
d = {frozenset(): 1}
key = set()
print(any(key == k for k in d)) # prints True
So trying to always return False
for unhashable objects would create an inconsistency. In theory, we could try to fall back to that for
loop, but that would lead to silent O(len(d)) performance degradation and defeat the benefits of a dict for little gain.
Also, even if key in d
was always False
for an unhashable key
, or if it had some kind of fallback, most instances where such a test even happens would still probably be due to bugs rather than deliberate. An exception is more useful than a False
result.
Upvotes: 0
Reputation: 78556
That's because checking for dictionary membership requires that the object be hashed first and then compared using __eq__
.
Consider the following toy example:
class Key(object):
def __hash__(self):
print('calling hash')
return 1
def __eq__(self, other):
print('calling eq')
return True
dct = {1: 2}
Key() in dct
# calling hash
# calling eq
In your example, the set()
does not get past the hash stage, and an error is correctly raised, rather than passed silently.
Upvotes: 1
Reputation: 362707
That's because the first thing Python needs to do in order to check whether it's in the dict is to attempt to hash the object.
As an alternate design decision, Python could potentially have handled this case; it would be done in dict.__contains__
implementation, catching the TypeError
and returning False
. But this provides less information to the user than just leaving the exception unhandled, and is therefore arguably less useful.
Upvotes: 2
Reputation: 9256
Making a set be a key in a dictionary is iffy because set is a mutable data structure.
What happens when you add some things to the set? Should 'a' continue to be treated as a key in the dict or should some other set with the original value of a be treated as a key instead?
To use a set-like structure as a key, use an immutable 'frozenset' instead. You cannot change the value of a frozenset after instantiating it and so python can safely allow it to be a key in a dict.
Upvotes: 0