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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1608842/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 22:41:57  来源:igfitidea点击:

Types that define `__eq__` are unhashable?

pythonhashpython-3.x

提问by Ram Rachum

I had a strange bug when porting a feature to the Python 3.1 fork of my program. I narrowed it down to the following hypothesis:

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

In contrast to Python 2.x, in Python 3.x if an object has an __eq__method it is automatically unhashable.

与 Python 2.x 不同的是,在 Python 3.x 中,如果一个对象有一个__eq__方法,它会自动成为不可散列的。

Is this true?

这是真的?

Here's what happens in Python 3.1:

以下是 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'

The follow-up question is, how do I solve my personal problem? I have an object ChangeTrackerwhich stores a WeakKeyDictionarythat points to several objects, giving for each the value of their pickle dump at a certain time point in the past. Whenever an existing object is checked in, the change tracker says whether its new pickle is identical to its old one, therefore saying whether the object has changed in the meantime. Problem is, now I can't even check if the given object is in the library, because it makes it raise an exception about the object being unhashable. (Cause it has a __eq__method.) How can I work around this?

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

采纳答案by Martin v. L?wis

Yes, if you define __eq__, the default __hash__(namely, hashing the address of the object in memory) goes away. This is important because hashing needs to be consistent with equality: equal objects need to hash the same.

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

The solution is simple: just define __hash__along with defining __eq__.

解决方案很简单:只需定义__hash__与定义一起__eq__

回答by newacct

This paragraph from http://docs.python.org/3.1/reference/datamodel.html#object.hash

这一段来自http://docs.python.org/3.1/reference/datamodel.html#object。散列

If a class that overrides __eq__()needs to retain the implementation of __hash__()from a parent class, the interpreter must be told this explicitly by setting __hash__ = <ParentClass>.__hash__. Otherwise the inheritance of __hash__()will be blocked, just as if __hash__had been explicitly set to None.

如果覆盖的类__eq__()需要保留__hash__()来自父类的实现 ,则必须通过设置__hash__ = <ParentClass>.__hash__. 否则继承__hash__()将被阻塞,就像__hash__被显式设置为 None 一样。

回答by Mark Rushakoff

Check the Python 3 manual on object.__hash__:

检查 Python 3 手册object.__hash__

If a class does not define an __eq__()method it should not define a __hash__()operation either; if it defines __eq__()but not __hash__(), its instances will not be usable as items in hashable collections.

如果一个类没有定义一个__eq__()方法,那么它也不应该定义一个__hash__()操作;如果它定义__eq__()但不是__hash__(),它的实例将不能用作可散列集合中的项目。

Emphasis is mine.

重点是我的。

If you want to be lazy, it sounds like you can just define __hash__(self)to return id(self):

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

User-defined classes have __eq__()and __hash__()methods by default; with them, all objects compare unequal (except with themselves) and x.__hash__()returns id(x).

用户定义的类默认有__eq__()__hash__()方法;与它们相比,所有对象都比较不相等(除了它们自己)并x.__hash__()返回id(x).

回答by falstro

I'm no python expert, but wouldn't it make sense that, when you define a eq-method, you also have to define a hash-method as well (which calculates the hash value for an object) Otherwise, the hashing mechanism wouldn't know if it hit the same object, or a different object with just the same hash-value. Actually, it's the other way around, it'd probably end up computing different hash values for objects considered equal by your __eq__method.

我不是 python 专家,但是当你定义一个 eq 方法时,你还必须定义一个哈希方法(它计算对象的哈希值),否则,哈希机制不知道它是击中了同一个对象,还是具有相同散列值的不同对象。实际上,情况正好相反,它可能最终会为您的__eq__方法认为相等的对象计算不同的哈希值。

I have no idea what that hash function is called though, __hash__perhaps? :)

我不知道那个哈希函数叫什么,__hash__也许吧?:)