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

Implement custom keys for dictionary so that 2 instances of same class match

我有 2 个 classes 实例,我想将其解析为字典中的同一个键:

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 的第二个实例应该匹配散列?这些哈希值不匹配是怎么回事?

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

请注意,在这种情况下,我不关心将 link 保留到字典中的原始对象,我可以使用一些不可用的键对象;重要的是他们决心相同。

编辑:

也许需要一些关于我想解决的实际案例的建议。

我有 classes 包含形状 (8,6) 的布尔值 np.arrays。我想对这些进行哈希处理,以便每当将此对象放入字典时,都会对这些值进行比较。我根据 this 的回答用它们做了一个位数组。我注意到那里有一个 __cmp__(感谢 thefourtheye 让我看到那里)。但是,我的 class 可以更新,所以我只想在我实际尝试将它放入字典时散列 np.array,而不是在启动时散列(从而存储可散列的位数组每当我 init 时,因为 np.array 可能会更新,这样散列就不再是真实的表示了)。我知道每当我更新 np.array 时,我也可以更新散列值,但我宁愿只散列一次!

__hash__ 只是确定将值放入哪个桶中。在桶内 python 总是调用 __eq__ 以确保它不会 return 一个元素恰好具有相同的哈希值,但实际上是不同的,所以你需要也实施您自己的 __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。 来自文档的 hashable 的定义:

An object is hashable if it has a hash value which never changes during its lifetime (it needs a __hash__() method), and can be compared to other objects (it needs an __eq__() method). Hashable objects which compare equal must have the same hash value.

Hashability makes an object usable as a dictionary key and a set member, because these data structures use the hash value internally.

您违反了 __hash____cmp____eq__ 之间的契约。引用 __hash__ documentation,

If a class does not define a __cmp__() or __eq__() method it should not define a __hash__() operation either; if it defines __cmp__() or __eq__() but not __hash__(), its instances will not be usable in hashed collections. If a class defines mutable objects and implements a __cmp__() or __eq__() method, it should not implement __hash__(), since hashable collection implementations require that a object’s hash value is immutable (if the object’s hash value changes, it will be in the wrong hash bucket).

User-defined classes have __cmp__() and __hash__() methods by default; with them, all objects compare unequal (except with themselves) and x.__hash__() returns an appropriate value such that x == y implies both that x is y and hash(x) == hash(y).

在您的例子中,两个对象的散列值相同,并且 hash Collision 在任何散列实现中都很常见。所以,Python用help__eq__方法对比查找的对象,发现实际查找的对象和已经存入的对象不一样,所以b in dicty returns 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 的值 data 会有所不同,如果它在稍后的时间点发生变化。

问题在于散列函数可能会导致冲突——不同的对象可能会产生相同的散列值。因此,最终检查对象是否存在于字典中仍然使用相等比较(即 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