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': 开发者_如何学Cu'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?
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
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 id
s, 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.
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')
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.
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.
精彩评论