定义__eq__的类型是不可散列的吗?


74

将功能移植到程序的Python 3.1 fork时遇到一个奇怪的错误。我将其缩小为以下假设:

与Python 2.x相比,在Python 3.x中,如果对象具有__eq__方法,则该对象将自动取消哈希。

这是真的?

这是Python 3.1中发生的情况:

>>> class O(object):
...     def __eq__(self, other):
...         return 'whatever'
...
>>> o = O()
>>> d = {o: 0}
Traceback (most recent call last):
  File "<pyshell#16>", line 1, in <module>
    d = {o: 0}
TypeError: unhashable type: 'O'

后续问题是,如何解决我的个人问题?我有一个对象ChangeTracker,该对象存储一个WeakKeyDictionary指向多个对象的对象,并为每个对象提供过去某个特定时间点的酱菜堆的值。每当检入现有对象时,变更跟踪器都会说出其新的泡菜与旧的泡菜是否相同,因此说明对象在此同时是否发生了变化。问题是,现在我什至无法检查给定的对象是否在库中,因为这使它引发有关该对象不可哈希的异常。(因为它有一种__eq__方法。)如何解决此问题?


3
如果提供一种__hash__方法会怎样?
ndim

我很惊讶地看到没人在讨论为什么这对Python 2来说不是问题
。– nehem

Answers:


78

是的,如果您定义__eq__,则默认值__hash__(即对内存中对象的地址进行哈希处理)将消失。这很重要,因为散列必须与相等性保持一致:相等的对象也需要对其进行散列。

解决方案很简单:只需将define__hash__和define一起定义即可__eq__


让我补充一下:我定义__hash__return id(self)
Ram Rachum 09年

29
@ cool-RR:id(self)in的使用__hash__()肯定是错误的,除非您定义__eq__()了按对象标识进行比较(这似乎毫无意义)。如何通过实际__eq__()实施来更新您的问题,以便我们建议对其进行__hash__()补充?
Denis Otkidach 09年

1
我也一直试图解决这个问题。我想出了这个:def __eq__(): return self.__dict__ == other.__dict__ def __hash__(): return hash(self.__dict__.values())
龟是可爱的

5
这不是一个很好的答案-不仅仅是默认哈希消失了-它是任何继承的哈希
埃里克


3

在以下位置查看Python 3手册object.__hash__

如果一个类没有定义一个__eq__()方法,它也不应该定义一个__hash__()操作。如果定义__eq__()__hash__()未定义,则其实例将不能用作可哈希集合中的项目。

重点是我的。

如果你想偷懒,听起来就像你可以定义 __hash__(self)return id(self)

用户定义的类默认具有__eq__()__hash__()方法。使用它们,所有对象比较不相等(它们自身除外)并x.__hash__()返回id(x)


5
当然,重载equals运算符的唯一原因是因为两个不同的对象有时可以比较equal(如果不相等,则不必再重载它)。在这种情况下,return id(self)哈希函数将被破坏(相等的对象必须对哈希进行相同的哈希,请参阅Martin的答案)。“我不在乎”哈希函数将是return 1。这很简单,可以满足所有条件(效率很低!)
Scott Griffiths

1

我不是python专家,但是当您定义eq方法时,您还必须定义一个hash方法(计算对象的hash值)是否有意义,否则,使用hashing机制不知道它是命中相同的对象还是具有相同哈希值的不同对象。实际上,这是相反的,它可能最终会为您认为相等的对象计算不同的哈希值__eq__方法。

我不知道该哈希函数是什么,__hash__也许吗?:)


6
仅供参考:我之前画过这个( mindmeister.com/10510492/python-underscore):在Python中的所有下划线方法思维导图。
jldupont

3
反之亦然。如果两个对象具有相同的哈希值,但不相等-很好。“哈希机制”(即字典)首先检查哈希,并且在相同哈希上还比较相等性。真正的问题以另一种方式出现:对象散列不同,但仍然比较相同。字典应该找到它们,但是找不到(或者您可能在字典中得到重复的键)。
马丁诉洛维斯案

@Martin诉Löwis:我意识到这一点,并在最后补充说,还是我所说的有微妙的区别?
falstro
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.