On the console I typed in
>>> class S(str): pass
...
>>> a = 'hello'
>>> b = S('hello')
>>> d = {a:a, b:b}
>>> d
{'hello': 'hello'}
>>> type(d[a])
<class '__main__.S'>
>>> type(d[b])
<class '__main__.S'>
I thought at first that the reason that d
only kept one pair was because hash(a)
and hash(b)
returned the same values, so I tried:
>>> class A(object):
... def __hash__(self):
... return 0
...
>>> class B(object):
... def __hash__(self):
... return 0
...
>>> d = {A():A(),B():B()}
>>> d
{<__main__.A object at 0x101808b90>: <__main__.A object at 0x101808b10>, <__main__.B object at 0x101808d10>: <__main__.B object at 0x101808cd0>}
Now I'm confused.
How come in the first code listing, d
only kept one pair, but in the second listing d
both keys were kept despite having same hash?
The two objects in your original example were collapsed not because they have the same hash, but because they compare equal. Dict keys are unique with respect to equality, not hash. Python requires that any two objects that compare equal must have the same hash (but not necessarily the reverse).
In your first example, the two objects are equal, since they both have the str
equality behavior. Since the two objects compare equal, they are collapsed to one. In the second example, they don't compare equal. By default user-defined classes use identity for equality --- that is, each object compares equal only to itself. So your two objects are not equal. It doesn't matter than they have the same hash.
The hash does not determine the unique key in a dictionary. In some ways, hash functions are an "implementation detail", in that they determine how the dictionary internally stores its entries. a == b implies hash(a) == hash(b), but the converse does not hold in general. Two keys also need to be equal to each other (when applying the == operator) to be treated as equivalent keys in a dictionary.
If you want a type to be hashable then you must also define __eq__()
. str
defines __eq__()
properly, but A
and B
do not.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With