Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explanation needed regarding explanation of hashable objects

Tags:

python

hash

Mark Ransom answered on a SO question about hashes here in SO:

[...] An object is hashable if it has a hash value which never changes during its lifetime. So by the official definition, anything mutable can't be hashable, even if it has a __hash__() function. My statement about both requirements being necessary is untrue, because being hashable already implies the requirement to be immutable.

I want to make sure, that I got that right - even as a non native speaker - so I hope someone corrects me if I got it wrong.

Assuming this class

class Author(object):
    def __init__(self, id, name, age):
        self.id = id
        self.name = name
        self.age = age

    def __eq__(self, other):
        return self.id==other.id\
               and self.name==other.name

    def __hash__(self):
        return hash(('id', self.id,
                     'name', self.name))

I understand, that __eq__ allows me to compare objects of this class with the == operator. From Marks answer I understand, that even if my object peter = Author(1, "Peter", 33) has a __hash__ it is not hashable because I potentially could do something like peter.age = 43 which means it is not immutable. So my objects of the class Author are not hashable and therefore not usable as keys in dictionarys for example? Did I get right or does it seem, that I need more explanation? :-)

like image 610
Aufwind Avatar asked Jul 11 '11 15:07

Aufwind


1 Answers

Instances of this class are hashable if you promise never to reset id or name on them. You can't guarantee that these attributes will never be reset, by the Python principle that "we are all consenting adults here", but it would be very bad style to offer methods that reset the attributes that __hash__ relies on.

E.g., if you offer a set_name method on instances of Author, but no set_id, then clients of the class can reasonably presume that __hash__ operates only on the id.

If you want to make it clear to clients of Author that they should not reset some attribute, you can label it private by prepending an _ to its name. If you then still want to offer (read-only) access to the attribute using its common name, you can make it a property:

class Author(object):
    def __init__(self, id, name, age):
        self._id = id
        self._name = name
        self.age = age      # ages tend to change, so mutable

    id = property(lambda self: self._id)
    name = property(lambda self: self._name)

    def __eq__(self, other):
        return self.id==other.id\
               and self.name==other.name

    def __hash__(self):
        return hash(('id', self.id,
                     'name', self.name))
like image 123
Fred Foo Avatar answered Oct 15 '22 04:10

Fred Foo