为字典实现自定义键,以便相同类的2个实例匹配

时间:2015-02-21 09:36:55

标签: python python-3.x dictionary hash

我有两个类的实例,我想解析为字典中的相同键:

class CustomClass(): 
    def __hash__(self):
        return 2

a = CustomClass()        
b = CustomClass()        

dicty = {a : 1}

这里,a和b不等于键:

>>> a in dicty
True
>>> b in dicty
False

哈希到底发生了什么?看起来CustomClass的第二个实例应该与散列相匹配?这些哈希值与之不匹配的是什么?

我刚才发现实际的课程正在被散列。那么如何为类添加自定义词典键(即当我尝试使用类作为词典的键时,应该如何存储以使a和b匹配)?

请注意,在这种情况下,我不关心保持字典中原始对象的链接,我可以使用一些不可用的密钥对象;只要重要的是他们解决问题。

编辑:

或许需要就我想要解决的实际案例提出一些建议。

我的类包含形状np.arrays的布尔(8,6)。我想对这些进行散列,这样无论何时将此对象放入字典中,都会对这些值进行比较。根据{{​​3}}回答,我从他们身上做了一个小小的问题。我注意到它有一个__cmp__(感谢thefourtheye显示我必须看那里)。但是,我的课程可以更新,所以我只想在我实际尝试将它放入字典中而不是在启动时(或者因此在每次我存储可存储的比特阵列时)对np.array进行哈希处理。 init ,因为np.array可能会被更新,因此哈希不再是真实的表示形式。我知道每当我更新np.array时,我也可以更新散列值,但我更喜欢只散列一次!

4 个答案:

答案 0 :(得分:5)

您违反了__hash____cmp____eq__之间的合同。引用__hash__ documentation

  

如果某个类未定义__cmp__()__eq__()方法,则不应定义__hash__()操作;如果它定义__cmp__()__eq__()但不定义__hash__(),则其实例将无法在散列集合中使用。如果一个类定义了可变对象并实现了__cmp__()__eq__()方法,那么它不应该实现__hash__(),因为hashable collection实现要求对象的哈希值是不可变的(如果对象的哈希值)更改,它将在错误的哈希桶中。)

     

默认情况下,用户定义的类具有__cmp__()__hash__()方法;与他们一起,所有对象比较不相等(除了他们自己)x.__hash__()返回一个适当的值,x == y同时暗示x is yhash(x) == hash(y)

在您的情况下,两个对象的哈希值相同,hash Collision在任何哈希实现中都很常见。因此,Python将正在查找的对象与帮助__eq__方法进行比较,并发现被搜索的实际对象与已存储的对象不同。这就是为什么b in dicty返回{{1 }}

因此,要解决您的问题,请定义自定义False功能,如此

__eq__

注意:对于给定对象, class CustomClass(): def __init__(self): self.data = <something> def __hash__(self): # Find hash value based on the `data` return hash(self.data) def __eq__(self, other): return self.data == other.data 值应始终相同。因此,请确保在最初分配后__hash__永远不会更改。否则,您永远无法从字典中获取对象,因为data hash的值将会有所不同,如果它在稍后的时间点发生变化。

答案 1 :(得分:1)

__hash__只确定将值放入哪个存储桶。在存储桶中,python始终调用__eq__以确保它不会返回恰好具有相同哈希值的元素,但实际上它不同,因此您需要实现自己的__eq__同样。

class CustomClass():
    def __hash__(self):
        return 2

    def __eq__(self, other):
        return hash(other) == hash(self)


a = CustomClass()     
b = CustomClass()     

dicty = {a : 1}

print a in dicty
print b in dicty
print "a" in dicty

答案 2 :(得分:1)

您应该实施__eq__方法来制作对象hashable。 来自doc的hashable的定义:

  

如果对象具有在此期间永远不会更改的哈希值,则该对象是可清除的   它的生命周期(它需要一个__hash __()方法),并且可以   与其他对象相比(它需要一个__eq __()方法)。可哈希   比较相等的对象必须具有相同的哈希值。

     

Hashability使对象可用作字典键和set成员,因为这些数据结构在内部使用哈希值。

答案 3 :(得分:1)

问题是哈希函数可能导致冲突 - 不同的对象可以产生相同的哈希值。因此,最终检查以确定对象是否存在于dict中仍然使用相等比较(即x == y)来完成。哈希值首先用于快速查找相关对象。

如果您想要您描述的行为,那么您也必须覆盖__eq__

例如

class CustomClass: 
    def __hash__(self):
        return 2
    def __eq__(self, other):
        return type(self) is type(other) and type(self) is CustomClass