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



我有两个类的实例,我想将它们解析为字典中的同一个键:

class CustomClass(): 
    def __hash__(self):
        return 2
a = CustomClass()        
b = CustomClass()        
dicty = {a : 1}

这里,a和b不等同于密钥:

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

hash到底发生了什么;CustomClass的第二个实例似乎应该与哈希匹配?这些散列不匹配是怎么回事?

我刚刚发现,真正的类就是被散列的类。那么,如何为类添加自定义字典键(即,当我尝试使用类作为字典键时,应该如何存储它以使a和b匹配)?

注意,在这种情况下,我不在乎在字典中保留到原始对象的链接,我可以处理一些不可用的键对象;重要的是他们决心保持不变。

编辑:

也许需要对我想解决的实际案件提出一些建议。

我有包含形状为(8,6)的布尔np.arrays的类。我想对这些值进行散列,这样每当这个对象被放入字典时,就会对这些值执行比较。根据这个答案,我用它们做了一个比特数组。我注意到它有一个__cmp__(感谢thefourtheye显示我必须看那里)。然而,我的类可以更新,所以我只想在实际尝试将np.array放入字典时对其进行散列,而不是在初始化时(因此,每当我init时都会存储可散列的位数组,因为np.arrays可能会更新,这样散列就不再是真实的表示了)。我知道每当我更新np.array时,我也可以更新哈希值,但我更喜欢只哈希一次!

您违反了__hash____cmp____eq__之间的合同。引用__hash__文件,

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

用户定义类默认有__cmp__()__hash__()方法;与它们相比,所有对象都比较不相等(除了它们自己),并且x.__hash__()返回一个适当的值,使得x == y意味着x is yhash(x) == hash(y)

在您的情况下,两个对象的哈希值是相同的,哈希冲突在任何哈希实现中都很常见。因此,Python将所查找的对象与help __eq__方法进行比较,发现实际搜索的对象与已存储的对象不相同。这就是b in dicty返回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在最初分配后从未更改。否则,您将永远无法从字典中获取对象,因为如果datahash值在以后的时间点发生变化,它将不同。

问题是哈希函数可能会导致冲突——不同的对象可能会产生相同的哈希值。因此,查看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

__hash__只是确定将值放入哪个存储桶中。在bucket中,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

您应该实现__eq__方法来使您的对象成为hashable。doc:中hashable的定义

如果对象的哈希值在它的生存期(它需要一个__hash__()方法),并且可以与其他对象相比(它需要一个__eq__()方法)。Hashable比较相等的对象必须具有相同的哈希值。

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

最新更新