Reputation: 3020
I am using custom objects as keys in python dictionary. These objects has some default hash and eq methods defined which are being used in default comparison But in some function i need to use a different way to compare these objects. So is there any way to override or pass a new comparer for these key comparison for this specific function only.
Updated: My class has following type of functionality ( here i can not edit hash method ,it will affect a lot at other places)
class test(object):
def __init__(self,name,city):
self.name=name
self.city=city
def __eq__(self,other):
hash_equality= (self.name==other.name)
if(not hash_equality):
#check with lower
return (self.name.lower()==other.name.lower())
def __hash__(self):
return self.name.__hash__()
my_dict={}
a=test("a","city1")
my_dict[a]="obj1"
b=test("a","city2")
print b in my_dict #prints true
c=test("A","city1")
print c in my_dict #prints false
print c in my_dict.keys() #prints true
# my_dict[c] throw error
This is the normal functionality. But in one specific method i want to override/or pass a new custom comparer where the new hash code is like
def __hash__(self):
return self.name.lower().__hash__()
so that c in my_dict
returns ture
or my_dict[c] will return "obj1"
Sorry for so many updates.
Like in sorting we can pass custom method as comparer , is there any way to do the same here.
Upvotes: 7
Views: 7111
Reputation: 91
Steps : Implement a custom key class and override hash and equality function.
e.g.
class CustomDictKey(object):
def __init__(self,
param1,
param2):
self._param1 = param1
self._param2 = param2
# overriding hash and equality function does the trick
def __hash__(self):
return hash((self._param1,
self._param2))
def __eq__(self, other):
return ( ( self._param1,
self._param2 ) == ( other._param1,
other._param2) )
def __str__(self):
return "param 1: {0} param 2: {1} ".format(self._param1, self._param2)
if name == 'main':
# create custom key
k1 = CustomDictKey(10,5)
k2 = CustomDictKey (2, 4)
dictionary = {}
#insert elements in dictionary with custom key
dictionary[k1] = 10
dictionary[k2] = 20
# access dictionary values with custom keys and print values
print "key: ", k1, "val :", dictionary[k1]
print "key: ", k2, "val :", dictionary[k2]
Refer the link Using custom class as key in Python dictionary for complete details.
Upvotes: 0
Reputation: 3020
now I am using custom dict(derived class of dict) which take comparer as parameter and i have overridden the contains and getitems() which checks and give value based on the comparer.
Upvotes: 0
Reputation: 11381
A little hack for this situation:
class test(object):
def __init__(self,name,city,hash_func=None):
self.name=name
self.city=city
self.hash_func = hash_func
def __eq__(self,other):
return self.__hash__()==other.__hash__()
def __hash__(self):
if self.hash_func is None:
return self.name.__hash__()
else:
return self.hash_func(self)
my_dict={}
a=test("a","city1")
my_dict[a]="obj1"
b=test("a","city2")
print b in my_dict #prints true
c=test("A","city1")
print c in my_dict #Prints false
c.hash_func = lambda x: x.name.lower().__hash__()
print c in my_dict #Now it prints true
You can't change the hash stored in the dict, but you can change the hash use for looking up. Of course, this leads to something weird like this
my_dict={}
a=test("a","city1")
my_dict[a]="obj1"
a.hash_func = lambda x: 1
for key in my_dict:
print key in my_dict # False
Upvotes: 0
Reputation: 76828
The only way to make this work is to create a copy of your dictionary using the new hash and comparison-function. The reason is that the dictionary needs to rehash every stored key with the new hash-function to make the lookup work as you desire. Since you cannot provide a custom hash-function to a dictionary (it always uses the one of the key-objects), your best bet is probably to wrap your objects in a type that uses your custom hash and comparison-functions.
class WrapKey(object):
__init__(self, wrapee):
self._wrapee = wrapee
__hash__(self):
return self._wrapee.name.lower().__hash__()
__eq__(self, other):
return self._wrapee.name == other._wrapee.name
def func(d):
d_copy = dict((WrapKey(key), value) for key, value in d.iteritems())
# d_copy will now ignore case
Upvotes: 4
Reputation: 318518
Have a look at the comparison methods you can define in an object.
Depending on what you want to do, __cmp__
might also be interesting.
Upvotes: 0