Object Integer mapping

Robert Kern robert.kern at gmail.com
Wed Jan 20 12:43:20 EST 2010


On 2010-01-20 11:18 AM, Richard Thomas wrote:
> On Jan 20, 4:43 pm, NighterNet<darkne... at gmail.com>  wrote:
>> Need help on python version 3.1.x. I can't seem to know what going on
>> a bit. The code that I check that the hash is different every time. Is
>> there a way to make the hash the same? I using as to check the class
>> is the same variables but if it different variable in the class that
>> it add to the array and return the index.
>>
>> # Generic Object->Integer mapping
>> # the object must be usable as a dictionary key
>> class ObjMap:
>>          def __init__(self):
>>                  self.dict = {}
>>                  self.next = 0
>>          def get(self, obj):
>>                  if obj in self.dict:
>>                          return self.dict[obj]
>>                  else:
>>                          id = self.next
>>                          self.next = self.next + 1
>>                          self.dict[obj] = id
>>                          return id
>>
>>          def items(self):
>>                  getval = operator.itemgetter(0)
>>                  getkey = operator.itemgetter(1)
>>                  return map(getval, sorted(self.dict.items(), key=getkey))
>>
>> class Point:
>>          def __init__(self):
>>                  self.x = 0
>>                  self.y = 0
>>                  self.z = 0
>>
>> points = ObjMap()
>>
>> Testme = Point()
>> Testme.x = 0
>> print(points.get(Testme))
>> Testme2 = Point()
>> Testme2.y = 1
>> print(points.get(Testme2))
>> Testme3 = Point()
>> Testme3.y = 1
>> print(points.get(Testme3))
>> Ttestme4 = Point()
>> Ttestme4.y = 1
>> print( points.get(Ttestme4))
>>
>> It keep adding new array from this but doesn't match hash. Need help
>> on how to fix this class code.
>
> You need to define how the Point class hashes. You can do this by
> writing a __hash__ method like so:
>
> class Point(object):
>      ...
>      def __hash__(self):
>          return hash(self.x) ^ hash(self.y) ^ hash(self.z)
>
> However you will also need to define how the Point class relates as
> equal using the __eq__ or __cmp__ methods.
>
> class Point(object):
>      ...
>      def __eq__(self, other):
>          return self.x == other.x and self.y == other.y and self.z ==
> other.z
>
> This addition makes sure that if two points a equivalent then they
> count as the same key in the dictionary.

I recommend a simpler approach that duplicates less code and makes it easier to 
maintain the __hash__/__eq__ invariants:

class Point(object):
     ...
     def _key(self):
         # The type(self).__name__ bit is optional, but usually handy.
         return (type(self).__name__, self.x, self.y, self.z)

     def __hash__(self):
         return hash(self._key())

     def __eq__(self, other):
         if not hasattr(other, '_key'):
             return False
         return self._key() == other._key()

It is also worth noting that once one allows hashing by value, one should treat 
the objects as immutable, so one should not change the attributes as the OP does 
in his example.

-- 
Robert Kern

"I have come to believe that the whole world is an enigma, a harmless enigma
  that is made terrible by our own mad attempt to interpret it as though it had
  an underlying truth."
   -- Umberto Eco




More information about the Python-list mailing list