简体   繁体   中英

Python dictionary keys(which are class objects) comparison with multiple comparer

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.

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

Steps : Implement a custom key class and override hash and equality function.

eg

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)

main method

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.

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.

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

现在我使用自定义dict(派生类的dict),它将comparer作为参数,我已经覆盖了containsgetitems (),它根据比较器检查和给出值。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM