My code looks something like this:
class SomeClass(str):
pass
some_dict = {'s':42}
>>> type(some_dict.keys()[0])
str
>>> s = SomeClass('s')
>>> some_dict[s] = 40
>>> some_dict # expected: Two different keys-value pairs
{'s': 40}
>>> type(some_dict.keys()[0])
str
Why did Python convert the object s
to the string "s" while updating the dictionary some_dict
?
Whilst the hash value is related, it is not the main factor.
It is equality that is more important here. That is, objects may have the same hash value and not be equal, but equal objects must have the same hash value (though this is not strictly enforced). Otherwise you will end up with some strange bugs when using dict
and set
.
Since you have not defined the __eq__
method on SomeClass
you inherit the one on str
. Python's builtins are built to allow subclassing, so __eq__
returns true, if the object would otherwise be equal were it not for them having different types. eg. 's' == SomeClass('s')
is true. Thus it is right and proper that 's'
and SomeClass('s')
are equivalent as keys to a dictionary.
To get the behaviour you want you must redefine the __eq__
dunder method to take into account type. However, when you define a custom equals, python stops giving you an automatic __hash__
dunder method, and you must redefine it as well. But in this case we can just reuse str.__hash__
.
class SomeClass(str):
def __eq__(self, other):
return (
type(self) is SomeClass
and type(other) is SomeClass
and super().__eq__(other)
)
__hash__ = str.__hash__
d = {'s': 1}
d[SomeClass('s')] = 2
assert len(d) == 2
print(d)
prints: {'s': 2, 's': 1}
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