Open AI - Opting Out
Open AI - Opting Out

Reputation: 24133

If an object is unhashable it's an error to ask if it's a dictionary key

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

Answers (4)

user2357112
user2357112

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

Moses Koledoye
Moses Koledoye

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

wim
wim

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

Aditya Mukherji
Aditya Mukherji

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

Related Questions