Bogdan
Bogdan

Reputation: 8246

What is the Python equivalent of Comparables in Java?

I have a dictionary of the following form:

{ <Category('Simulate', 'False', 'False', 'False', 'INTERMEDIATE')>: {'link': u'/story/4/tvb-adapters-simulator-simulatorAdapter/SimulatorAdapter', 'name': u'Simulate'}, 
  <Category('View Results', 'True', 'False', 'True', 'INTERMEDIATE')>: {'link': '/story/step/3', 'name': u'View Results'}, 
  <Category('Analyze', 'True', 'False', 'False', 'FINAL')>: {'link': '/story/step/2', 'name': u'Analyze'}}

Category is a class representing an instance from a database. Now I have the following instance:

    <Category('Analyze', 'True', 'False', 'False', 'FINAL')>

Now this is not the same instance. By this I mean, I get all the values from the database and create the dictionary. Then after a while I get an id and retrieve the instance from the database. Now they are not the same objects. I now have to check if it's in the dictionary, but:

instance in disctionary

Will return false. Now I could go the ugly way and iterate over the dictionary checking if all the values match, however does Python have a more clever way to do this? I mean something like the equivalent of Comparable in Java?

Upvotes: 14

Views: 16470

Answers (5)

unutbu
unutbu

Reputation: 880547

Instead of using instances of Category (e.g. Category('Analyze', 'True', 'False', 'False', 'FINAL')) as the keys in your dictionary, it sounds like you should be using the associated tuple (e.g. ('Analyze', 'True', 'False', 'False', 'FINAL')).

If you really do want to use instance of Category as the keys in the dictionary, you'll need to define both the __hash__ and __eq__ methods. For example:

class Category(object):
    def __init__(self,*args):
        self.args=args
    def __hash__(self):
        # Note that this assumes that Category's hash value is immutable
        # i.e. self.args does not change.
        return hash(self.args)
    def __eq__(self,other):
        return self.args == other.args

a=Category('Analyze', 'True', 'False', 'False', 'FINAL')
b=Category('Analyze', 'True', 'False', 'False', 'FINAL')

mydict={a:1}

a and b are distinct instances, so they have different ids, but their hash values are the same:

assert id(a) != id(b)
assert hash(a)==hash(b)

This shows b is an acceptable key in mydict:

print(mydict[a])
# 1
print(mydict[b])
# 1

PS. Don't bother with __cmp__. In Python 3:

The cmp() function should be treated as gone, and the __cmp__() special method is no longer supported. Use __lt__() for sorting, __eq__() with __hash__(), and other rich comparisons as needed.

Upvotes: 3

Jochen Ritzel
Jochen Ritzel

Reputation: 107686

As you can apparently put your category instances into a dict, you must have overwritten __hash__ already. All you need now is __eq__:

class Category(object):
    # you must have overwritten `__hash__` like this already
    def __hash__(self):
        return hash((self.attr1, self.attr2, ... ))

    # now you just need tis
    def __eq__(self, other):
        return isinstance(other, Category) and \
               (self.attr1, self.attr2, ... ) == (other.attr1, other.attr2, ... )

What you should really do is throw that whole class away and use a collections.namedtuple instead:

Category = collections.namedtuple('Category', 'attr1 attr2 attr3')

Upvotes: 2

pyroscope
pyroscope

Reputation: 4158

For in and other comparison operators to work, you have to implement __hash__ and __cmp__ (or the "rich" comparison methods like __eq__). See the Python reference.

Upvotes: 1

Dr McKay
Dr McKay

Reputation: 2568

First: use True and False (boolean properties) instead of 'True' and 'False' (string properties).

Generally, you can make everything comparable in Python. You just have to define specific methods (like __eq__, __lt__, etc.) for your class.

So, let's say I want to compare instances of class A, and the comparison should be just case-insensitive string comparison of s member:

class A(object):
    def __init__(self, s=''):
        self.s = s

    def __eq__(self, other):
        return self.s.lower() == other.s.lower()

a = A('aaaa')
b = A('AAAA')
print a == b # prints True
b = A('bbbb')
print a == b # prints False

Upvotes: 17

Jerome
Jerome

Reputation: 2370

There could be shortcuts, like using tuples, but the generic answer to your question is: implement __eq__() for your class, so that cmp() will use it instead of testing for identity.

Upvotes: 1

Related Questions