Preface
I understand that dict
s/set
s should be created/updated with hashable objects only due to their implementation, so when this kind of code fails
>>> {{}} # empty dict of empty dict
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: unhashable type: 'dict'
it's ok and I've seen tons of this kind of messages.
But if I want to check if some unhashable object is in set
/dict
>>> {} in {} # empty dict not in empty dict
I get error as well
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: unhashable type: 'dict'
Problem
What is the rationale behind this behavior? I understand that lookup and updating may be logically connected (like in dict.setdefault
method), but shouldn't it fail on modification step instead of lookup? Maybe I have some hashable "special" values that I handle in some way, but others (possibly unhashable) -- in another:
SPECIAL_CASES = frozenset(range(10)) | frozenset(range(100, 200))
...
def process_json(obj):
if obj in SPECIAL_CASES:
... # handle special cases
else:
... # do something else
so with given lookup behavior I'm forced to use one of the options
- LBYL way: check if
obj
is hashable and only after that check if it is one ofSPECIAL_CASES
(which is not great since it is based onSPECIAL_CASES
structure and lookup mechanism restrictions, but can be encapsulated in separate predicate), EAFP way: use some sort of utility for "safe lookup" like
def safe_contains(dict_or_set, obj): try: return obj in dict_or_set except TypeError: return False
- use
list
/tuple
forSPECIAL_CASES
(which is notO(1)
on lookups).
Or am I missing something trivial?