我最近发现了一个有趣的发现,即某些因素会影响对象/类实例的哈希性。我想知道如何以及为什么?
例如,我有一个名为ListNode
的链表类:
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
def __repr__(self):
if self.next:
return "{}->{}".format(self.val, repr(self.next))
else:
return "{}".format(self.val)
# def __eq__(self, other):
# if not self and not other:
# return True
# elif not self or not other:
# return False
# else:
# return self.val == other.val and self.next == other.next
# def __eq__(self, other):
# return str(self) == str(other)
注意,我阻止了__eq__
方法。
现在,如果我创建一个实例:
A = ListNode(1)
B = ListNode(2)
C = ListNode(3)
A.next = B
B.next = C
print(hash(A))
然后它是可哈希的,但是,每次运行时,我的确得到不同的输出编号。
现在,如果我取消阻止__eq__
方法,那么突然该方法将不再可哈希化。为什么?
似乎hash
方法将使用__eq__
。以及如何知道启用__eq__
后不能将其散列?
其他:
如果我编写__eq__
方法只是比较两个链表的str
版本(第二个__eq__
方法),我认为这可以解决问题,因为通过转换链表到string
中,它变成可散列的数据,但我仍然收到unhashable
错误消息
谢谢!
根据@ juanpa.arrivillaga的评论:
__eq__
将删除默认的__hash__
方法,使其无法散列。
因此,我添加了自己的__hash__
方法:
def __hash__(self):
return hash(id(self))
这解决了问题,并使ListNode
在启用__eq__
的情况下再次可散列。
答案 0 :(得分:2)
如果一个类没有定义__eq __()方法,它也不应该定义__hash __()操作;如果它定义__eq __()而不是__hash __(),则其实例将不能用作可哈希集合中的项目。
(...)
重写__eq __()但未定义__hash __()的类会将其__hash __()隐式设置为None。当类的__hash __()方法为None时,该类的实例将在程序尝试检索其哈希值时引发适当的TypeError,并且在检查isinstance(obj,collections.abc.Hashable)时也将被正确标识为不可哈希)。 1
因此__eq __()方法的引入将__hash __()设置为None。您可以添加一个自定义哈希,以允许进行上述构建:
def __hash__(self):
return self.val
更多信息,请点击此处:https://docs.python.org/3/reference/datamodel.html#object.hash