在Dictionary键中对象不被认为是相同的 - 但实现了__eq__

时间:2015-08-28 20:57:56

标签: python dictionary

以下代码给出了错误消息:

    class Test(object):

    def __init__(self, test = 0):
        self.test = test

if __name__ == '__main__':
    t1 = Test(1)
    t2 = Test(2)
    t3 = Test(3)
    t4 = Test(1)
    my_dict = {}
    my_dict[t1] = 1
    my_dict[t2] = 2
    my_dict[t3] = 3

    print(my_dict[t4])

Traceback (most recent call last):
  File "C:\Users\Alexander\Documents\Visual Studio 2015\Projects\PipeProcessTest
\PipeProcessTest\DictionaryKeys.py", line 16, in <module>
    print(my_dict[t4])
KeyError: <__main__.Test object at 0x0000000002F18080>

这是因为python将t1和t4视为不同的对象。但是,当我使用以下代码实现比较运算符&#39; eq &#39;时:

def __eq__(self, other):
        if self.test == other.test:
            return True
        else:
            return False

我收到另一条错误消息,&#34;不可用类型:&#39;测试&#39;&#34;告诉我,现在字典不能散列Test对象。我如何解决这个问题,以便Python能够识别t1和t4是否相同,还能够散列Test对象?

4 个答案:

答案 0 :(得分:7)

除了__hash__之外,您还需要实施__eq__。有关如何执行此操作的说明,请参阅the documentation。要记住的主要事情是比较相等的对象必须具有相同的哈希值。因此,如果您只想通过查看test属性来比较相等性,那么您的__hash__也只需要使用test属性。搜索有关__hash____eq__的信息也会在此网站上发现此前的许多问题。

答案 1 :(得分:4)

<强> 1。这是因为python将t1和t4视为不同的对象。但是,当我使用以下代码实现比较运算符&#39; eq&#39;

当您在python中执行以下操作....

class Test(object):

    def __init__(self, test = 0):
        self.test = test

if __name__ == '__main__':
    t1 = Test(1)
    t2 = Test(2)
    t3 = Test(3)
    t4 = Test(1)
    my_dict = {}
    my_dict[t1] = 1
    my_dict[t2] = 2
    my_dict[t3] = 3

这意味着您实际上是在尝试使用dict个对象创建Test,Python会首先检查keys是否为hashable

Python中的任何objecthashable方法返回任何integer值时都为obj.__hash__()。在python中,默认情况下,所有用户定义的类都会获得一些id(self)的哈希值。

显然,当您获得id值为hash时,它们会看起来像这个值8772302607193。如果我们构建hash表,那么这些id可能就像这样......

让我们假设id是这样的..

id(t1) = 1
id(t2) = 4   # These are just assumptions.
id(t3) = 10  # actual id's are long values.
id(t4) = 20

这是hash表的构造方式....

    hash     Actual
    value    value  
    ------------------
    | 1    |  t1     |
    ------------------
    | 2    | NULL    |
    ------------------   # Consider that you've respective values are
    | 3    | NULL    |   # here, just for representation I've mentioned
    ------------------   # t1, t2, t3, t4, ..
    | 4    | t2      |
    ------------------
           |
           |
    ------------------
    | 10   |  t3     |
    ------------------
           |
         SO ON

像这样,您的hash表格会被构建,所以当您尝试获取t4的值时,只需尝试my_dict[t4]。根据假设hash哈希值为t4,首先通过调用t4.__hash__()检查t4 2020

获取哈希值20后,它检查哈希表,索引为20,因为我们没有用KeyError插入任何值,Python只会引发KeyError异常,这是您在尝试my_dict[t4]时获得__hash__的原因。

此处的另一个方案:

如果您尝试覆盖Test方法f class Test(object): def __init__(self, test = 0): self.test = test def __hash__(self): self.test # You are just returning the same value if __name__ == '__main__': t1 = Test(1) t2 = Test(2) t3 = Test(3) t4 = Test(1) my_dict = {} my_dict[t1] = 1 my_dict[t2] = 2 my_dict[t3] = 3 类并继续执行下面所做的相同操作..

overloaded

由于我们用t1 = 1 , t2 = 2, t3 = 3, t4 = 1 哈希方法返回与初始化相同的值,下面是我们得到的哈希值

hash

当我们使用same hash value设置多个值时,这就是 hash Actual value value ------------------ | 1 | [t1,t4] | # List of values for the same hash value. ------------------ | 2 | t2 | ------------------ # Consider that you've respective values are | 3 | t3 | # here, just for representation I've mentioned ------------------ # t1, t2, ... | 4 | NULL | ------------------ | SO ON 表的显示方式。

my_dict[t4]

在这种情况下,当您尝试获取t4.__hash__()时,如第一次检查哈希值1之前所述,返回index。现在,在hash table的{​​{1}} 1处进行Python dict检查,它会获得多个值[t1, t4]

当您使用相同__eq__的多个值时,hash value可帮助您识别对象。你可以这样做,以避免这种情况......

class Test(object):
    def __init__(self, test = 0):
        self.test = test
    def __hash__(self):
        return self.test
    def __eq__(self, other):
        return self is other

在您的情况下,您只需要验证self.test值即可获取对象...

class Test(object):

    def __init__(self, test = 0):
        self.test = test
    def __hash__(self):
        return self.test
    def __eq__(self, other):
        return other.test == self.test

这是你管理你的dict值的方法!

答案 2 :(得分:3)

您只需要在self.test方法中返回__hash__,以便您的对象哈希值基于其测试属性,因此t1和t4将具有相同的哈希值并且在t1或t4上具有dict查找将返回相同的值:

   def __init__(self, test = 0):
        self.test = test
    def __eq__(self, other):
        return self.test == other.test
    def __hash__(self):
        return self.test

eq中不需要if / else,您只需返回self.test == other.test的结果

In [2]: t1 = Test(1)   
In [3]: t2 = Test(2)   
In [4]: t3 = Test(3)   
In [5]: t4 = Test(1)
In [6]:  my_dict = {}    
In [7]: print(t1 == t4)
True   
In [8]: my_dict[t1] = 1    
In [9]: my_dict[t2] = 2    
In [10]: my_dict[t3] = 3
In [11]: print(my_dict[t4])
1

答案 3 :(得分:0)

对于每个人,这是一个不错的示例:

class NonComparable(object):
    def __init__(self):
        pass

# Convert a cmp= function into a key= function 
class Comparable(object): 
    def __init__(self, val, hash_lambda, cmp_lambda): 
        self.val = val
        self.hash_lambda = hash_lambda
        self.cmp_lambda = cmp_lambda

    def __hash__(self): 
        return self.hash_lambda(self.val)

    def __eq__(self, other): 
        return self.cmp_lambda(self.val, other.val) == 0
        # return id(self) == id(other)

# driver code 
if __name__ == "__main__": 
    # __hash__ is available even if you don't implement it, it'd return hash(nc1), not id(nc1). hash() isn't available by default.
    # Note also,
    # id(123) = an integer
    # hash(456) = 456
    # So, hash(id(nc1)) = id(nc1)
    nc1 = NonComparable()
    nc1_hash = nc1.__hash__()

    nc2 = NonComparable()
    nc2_hash = nc2.__hash__() 

    moduli = 2
    hash_lambda = lambda x : x * x
    cmp_lambda_by_val = lambda x,y : x == y
    cmp_lambda_by_objid = lambda x,y : id(x) == id(y)

    c1 = Comparable(5, hash_lambda, cmp_lambda_by_val) # hash = 5*5 = 25
    c2 = Comparable(8, hash_lambda, cmp_lambda_by_val) # hash = 8*8 = 64

    # same hash code (To simulate hash collision), but compare by Equal based on id(obj)
    c3 = Comparable(5, hash_lambda, cmp_lambda_by_objid) 
    c4 = Comparable(5, hash_lambda, cmp_lambda_by_objid) 

    dict1 = {}
    dict1[nc1] = nc1
    dict1[nc2] = nc2
    res = dict1[nc1]

    dict2 = {}
    dict2[c1] = c1
    dict2[c2] = c2
    res = dict2[c1]

    dict3 = {}
    dict3[c3] = c3
    dict3[c4] = c4
    res = dict3[c3]
    # id(c3) == id(res)
    pass

参考

Objects are not considered the same in Dictionary keys - but __eq__ is implemented

https://www.programiz.com/python-programming/methods/built-in/hash

https://dbader.org/blog/python-dictionaries-maps-and-hashtables

https://zhuanlan.zhihu.com/p/37643853