0

I am trying to implement the hash definition suggested here but I am getting an error when trying to use hash on the class. The class is defined as so:

class Metabolite:
    def __init__(self, name_in='', id_in='', synonyms_in=[], InChIKey_in='', formulae_in=[], charge_in='', ecs_in=[], mtk_in = [],  mtb_in = '', mtm_in = '', mts_in = '', bkm_id_in = '', source_in = ''):
        self.name = name_in
        self.id = id_in
        self.formulae = formulae_in
        self.inchikey = InChIKey_in
        self.synonyms = synonyms_in
        self.charge = charge_in
        self.ecs = ecs_in
        self.mtb = mtb_in
        self.mtk = mtk_in
        self.mtm = mtm_in
        self.mts = mts_in
        self.bkm_id = bkm_id_in
        self.source = source_in

    def key(self):
        return self.id, self.inchikey, self.mts, self.mtk, self.mtb, self.mtk

    def __key(self):
        return tuple([self.id, self.inchikey, self.mts, self.mtk, self.mtb, self.mtk])

    def __eq__(self, other):
        return self.__key() == other.__key()

    def __hash__(self):
        return hash(tuple(self.__key()))

However on creating an instance of this class and asking for a hash I get the following:

>>> met = Metabolite('this_metabolite', '10002', 'AADBRHFDG')
>>> hash(met)

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-8-7941e6f25128> in <module>()
----> 1 hash(met)

classes.py in __hash__(self)
     27 
     28     def __hash__(self):
---> 29         return hash(tuple(self.__key()))
     30 
     31 

TypeError: unhashable type: 'list'

despite my desperate attempts in the class definition to force the type to be a (hashable) tuple. I am new to classes in Python, any help would be greatly appreciated.

When run in IDLE, the error comes up as:

Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    hash(met)
  File "<string>", line 27, in __hash__
TypeError: unhashable type: 'list'

SOLVED: thanks to those who responded.

A tuple containing an empty list will not work:

>>> my_tuple = tuple(('a',[]))

>>> type(my_tuple)

tuple

>>> hash(my_tuple)

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-28-f63af41cc75b> in <module>()
----> 1 hash(my_tuple)

TypeError: unhashable type: 'list'

so I had to create a list of my variables before returning them in __key():

def __key(self):
    key_list = []
    key_list.append(self.id)
    key_list.append(self.inchikey)
    key_list.append(self.mts)
    key_list.append(self.mtb)
    for entry in self.mtk:
        key_list.append(entry)
    return tuple(key_list)
Community
  • 1
  • 1
Will Bryant
  • 521
  • 5
  • 17
  • I just ran your class in idle, and I don't get this error. Instead, I get 301521507 as a result. – Justin O Barber Jan 29 '13 at 13:14
  • I didn't get an error either, what version of python are you running? – Hoopdady Jan 29 '13 at 13:24
  • 1
    Same here, no errors, check your code without IPython, it is known for its side-effects. – Rostyslav Dzinko Jan 29 '13 at 13:25
  • OK thanks a lot, this was just a toy example of what I had originally run and now I can't get it to reproduce the error ... have edited the post with my actual code which throws up the error. AND I'll check it in another interpreter – Will Bryant Jan 29 '13 at 13:29
  • 1
    @WillBryant: If your question is solved, it's a good idea to *answer your own question* and accept it (That gets it off the list of unanswered questions :-) – David Robinson Jan 29 '13 at 14:13
  • A tuple containing a list is not *seen as a list*; its elements get hashed separately to produce the final hash value. Btw., you might want to store `self.__key` as a tuple instead of a list. Your current code pretends that a mutable object is immutable for the purposes of hashing. I'd consider that a hack. – Fred Foo Jan 29 '13 at 14:38

1 Answers1

1

The problem is that the mtk attribute is a list (by default). Mutable sequence types cannot be hashed and since your key contains mtk (twice), the key is not hashable (even if the key itself is a tuple).

If you modify your key generation as follows, then it becomes hashable (but it might prove seriously inefficient):

def __key(self):
    mtk = tuple(self.mtk)
    return tuple([self.id, self.inchikey, self.mts, mtk, self.mtb, mtk])

This returns in Python 2.7 (code pasted from your question with above modification):

>>> met = Metabolite('this_metabolite', '10002', 'AADBRHFDG')
>>> hash(met)
7276685348836095537
isedev
  • 18,848
  • 3
  • 60
  • 59