← Back to team overview

oship-dev team mailing list archive

RES: Hash method

 

Hi Wagner,

 

My quick answer to your doubt would be: you are right, you would have to
create a new dictionary whenever you change its data. 

But this is the way the EHR works in openEHR: every composition and every
demographic object is versioned, meaning that you don´t delete or overwrite
older elements, you just create a new version of it.  I think that you don´t
even need to define a rehash method.

 

As to the __ne__ method,  now I tend to agree with you. If everyone agrees,
then we should add this recommendation to the implementation guide. Do we
have one?

 

Cheers

 

Sergio

 

De: Wagner Francisco [mailto:wagnerfrancisco@xxxxxxxxx] 
Enviada em: terça-feira, 9 de março de 2010 00:52
Para: Sergio Miranda Freire
Cc: OSHIP-Dev
Assunto: Re: [Oship-dev] Hash method

 

Hello Diego and Sergio, thanks for your replies.

Sergio, you gave us a very good example. I understood the problem and the
only doubt now is how to recalculate the hash if some attribute value
changes after we put it in a dictionary (if this is going to be necessary).
We don't have a "rehash" function in python dictionary. Just to show my
doubt, it's something like this:

class SpringDetector:
    def detectSpring(self):
        map = {}
        g0 = Groundhog(0);
        g1 = Groundhog(1);
        map[g0] = Prediction();          
        map[g1] = Prediction();
        print map    
        g1.number = 6  # changing the value...   
        print 'Looking up prediction for ' + str(g1)
        if (g1 in map):
            print map[g1]
        else:
            print 'key not found'
            
I searched the web and the only way I saw to solve this problem is to create
a new dictionary... something like this:

def rehash(d): 
  return dict(d.iteritems())

class SpringDetector:
    def detectSpring(self):
        map = {}
        g0 = Groundhog(0);
        g1 = Groundhog(1);
        map[g0] = Prediction();          
        map[g1] = Prediction();
        print map    
        g1.number = 6  # changing the value...   
        map = rehash(map)
        print 'Looking up prediction for ' + str(g1)
        if (g1 in map):
            print map[g1]
        else:
            print 'key not found'
            
            
I suggested to implement the __ne__ method because usually when we are
programming we expect that != is the negation of == (at least I expect). But
if we don't implement __ne__ it won't be true.


Thanks again,


Wagner F. Mezaroba. 

2010/3/8 Sergio Miranda Freire <sergio@xxxxxxxxxxxxxxx>

Hi Wagner,

 

I think all classes in Oship are mutable. Consider the following use case:

A person has a set of names. Each name is modeled as a class Name with two
attributes: firstName and lastName. Then you create a instance of Person p1,
and two instances of the Name class (Mary Smith and Mary Hopkins) and add
them to the set of names of p1. Suppose you consider that two names are
equal if their firstName and lastName are both equals.  The set of names of
a person then should not contain two equal names (by the definition of set).

 

If you don´t redefine __eq__ in your Name class, the program will consider
two names as equals if they are the same object. Two different instances of
Name with the same firstName and lastName would be different according to
the default implementation of __eq__. Therefore in this case we should
redefine __eq__. But we should also redefine __hash__, otherwise it would
not be based on the attributes used to establish equality between two
objects, and you would have problems, for example in using the object as a
key to a dictionary. 

 

I followed the link to the documentation and I don´t agree with the
statement that if “you redefine __eq__ for a mutable class, then you should
not redefine __hash__”. 

In the health applications I  think that we will base our sets and
dictionaries keys on object attributes’ values, not on objects’ ids.
Therefore we need to redefine both __eq__ and __hash__.

Below is a simple program that illustrates what I said. In the first
program, we use the default __eq__ and __hash__ implementations. See that
the key is not found and two Groundhogs (marmota in Portuguese) with the
same number is added to the set. In the second program, the __eq__ and
__hash__ are redefined as a function of the number attribute. See that the
key is found and there are no identifical groundhogs in the set. In fact if
I redefine __eq__ but do not redefine __hash__ the program doesn´t run. Try
it.

 

There are proposals to calculate the hash function in the literature. If you
change some attribute value, then the hash value will have to be
recalculated. 

I do not see the need to implement __ne__. Why should we?

 

Regards,

 

Sergio

 

________________________

Using default __eq__ and __hash__

 

import random

 

class Groundhog:

 

    def __init__(self, value):

        self.number = value

                               

    def __repr__(self):

        return 'Groundhog #' + str(self.number);

 

class Prediction:

  

    def __init__(self):

        rand = random.randint(1,10)

        self.shadow = (rand > 5)

                               

    def __repr__(self):

                if (self.shadow is False):

            return 'Early Spring'

        else:

            return 'Six more weeks of Winter!'

                               

class SpringDetector:

 

    def detectSpring(self):

        map = {}

        for i in range (0,9):

            map[Groundhog(i)] = Prediction();

        print map

        gh = Groundhog(3)

        print 'Looking up prediction for ' + str(gh)

        if (gh in map):

            print map[gh]

        else:

            print 'key not found'

 

if __name__ == "__main__":

    sd = SpringDetector()

    sd.detectSpring()

 

Output:

 

{Groundhog #1: Early Spring, Groundhog #3: Six more weeks of Winter!,
Groundhog #4: Early Spring, Groundhog #5: Early Spring, Groundhog #6: Early
Spring, Groundhog #7: Six more weeks of Winter!, Groundhog #8: Early Spring,
Groundhog #2: Early Spring}

Looking up prediction for Groundhog #3

key not found

set([Groundhog #0, Groundhog #3, Groundhog #8, Groundhog #1, Groundhog #5,
Groundhog #3, Groundhog #6, Groundhog #7, Groundhog #2, Groundhog #4])

 

_________________________________________________

“””Program redefining __eq__ and __hash__”””

 

import random

 

class Groundhog:

 

    def __init__(self, value):

        self.number = value

                               

    def __repr__(self):

        return 'Groundhog #' + str(self.number);

 

    def __hash__(self):

        return self.number

 

    def __eq__(self, o):

        if  isinstance(o, Groundhog):

            return self.number == o.number

        else:

            return false

 

class Prediction:

  

    def __init__(self):

        rand = random.randint(1,10)

        self.shadow = (rand > 5)

                               

    def __repr__(self):

                if (self.shadow is False):

            return 'Early Spring'

        else:

            return 'Six more weeks of Winter!'

                               

class SpringDetector:

 

    def detectSpring(self):

        map = {}

        for i in range (0,9):

            map[Groundhog(i)] = Prediction();

        print map

        gh = Groundhog(3)

        print 'Looking up prediction for ' + str(gh)

        if (gh in map):

            print map[gh]

        else:

            print 'key not found'

 

if __name__ == "__main__":

    sd = SpringDetector()

    sd.detectSpring()

 

 

Output:

 

{Groundhog #1: Six more weeks of Winter!, Groundhog #2: Early Spring,
Groundhog #3: Six more weeks of Winter!, Groundhog #4: Six more weeks of
Winter!, Groundhog #5: Early Spring, Groundhog #6: Early Spring, Groundhog
#7: Six more weeks of Winter!, Groundhog #8: Six more weeks of Winter!}

Looking up prediction for Groundhog #3

Six more weeks of Winter!

set([Groundhog #0, Groundhog #1, Groundhog #2, Groundhog #3, Groundhog #4,
Groundhog #5, Groundhog #6, Groundhog #7, Groundhog #8])

 

De: oship-dev-bounces+sergio=lampada.uerj.br@xxxxxxxxxxxxxxxxxxx
[mailto:oship-dev-bounces+sergio <mailto:oship-dev-bounces%2Bsergio>
=lampada.uerj.br@xxxxxxxxxxxxxxxxxxx] Em nome de Wagner Francisco
Enviada em: sábado, 6 de março de 2010 01:26
Para: OSHIP-Dev
Assunto: [Oship-dev] Hash method

 

Hello,

As we talked this week, we need to correct some parts of some Information
Models we are implementing. Sergio suggested that we implemented the
__hash__ method in some classes. I was studying about this method
(http://docs.python.org/reference/datamodel.html#object.__hash__), and I saw
that if we are writing a mutable class, we shouldn't override __hash__
method. Apparently, classes like EhrAccess and EhrStatus are mutable
classes, right? We can change their properties, so it would be hard to
calculate their hashcode. What do you think about it?

Changing the subject, in python the operator != isn't the negation of ==. So
if we are going to implement the __eq__ method it would be interesting to
implement the __ne__ method too. 

Cheers,

Wagner F. Mezaroba.

 


Follow ups

References