What is the reason that hash
never returns -1?
Example 1:
for i in range(-3, 4):
print(hash(i))
Outputs:
-3
-2
-2 <----- Notice this
0
1
2
3
Example 2:
class A(int):
def __hash__(self) -> int:
return -super().__hash__()
for i in range(-3, 4):
print(hash(A(i))
Outputs:
3
2
2 <----- Notice this
0
-2 <----- Notice this
-2
-3
In this case, both the super().__hash__()
method is replacing its -1
results to -2
, and A.__hash__()
is doing the same, so we end up with two -2
s and two 2
s.
Usage
If somneone is curious why I need this, I have two classes which are subclasses of int for limited ranges, lets say 1-20 and 1-100. Instances of this classes need to be used as keys in some dicts so they need to be hashable. I was trying to avoid having a nested dict by negating the hash of one of the ranges, so that one uses positive integers as hashes and the other negative ones, therefore being different keys:
from abc import ABCMeta, abstractmethod
class Range(int, meta=ABCMeta):
def __init__(self, *args, **kwargs):
super().__init__():
if not (self.min <= self <= self.max):
raise ValueError
@classmethod
@property
@abstractmethod
def min(self) -> int:
raise NotImplementedError
@classmethod
@property
@abstractmethod
def max(self) -> int:
raise NotImplementedError
class Range100(Range):
min = 1
max = 100
class Range20(Range):
min = 1
max = 20
def __hash__(self) -> int:
return -super().__hash__()
The Range
class and subclasses have been simplified, there are several aditional common methods and several other abstract methods.
Obviosuly, my case has a simple solution, offsetting the negative values so that they avoid the conflicting -1
, but I was curious why this happens at all.