【问题标题】:How to tell if an object/instance is hashable如何判断对象/实例是否可散列
【发布时间】:2019-05-24 19:44:45
【问题描述】:

我最近发现了一个有趣的观察结果,即有些事情会影响类的对象/实例的哈希性。我想知道如何以及为什么?

例如,我有一个名为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__mehtod:

def __hash__(self):
    return hash(id(self))

这解决了问题并在启用__eq__ 的情况下再次使ListNode 可散列。

【问题讨论】:

  • 无论如何,因为如果你定义一个__eq__,你必须定义一个兼容的__hash__。如果不定义__eq__,用户自定义类型默认基于身份哈希
  • @juanpa.arrivillaga 好的,在逻辑上它是有道理的。它确实有效。

标签: python hash


【解决方案1】:

如果一个类没有定义一个 __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

【讨论】:

  • 好吧,也许我可以创建自己的哈希方法,但这仍然不能解释为什么以及如何__eq__ 会影响哈希率。
  • Hashable 并不意味着不可变。只要不考虑任何可变部分,就可以为可变对象定义有效的哈希函数。
  • Hashable absolutely does mean immutable. 是的,第三方开发人员在技术上可以违反此语义约束——但这样做会使生成的类型不确定地疯狂并且在最常见的可散列上下文中不可用(例如,dict 键、set 项目),这很糟糕。因此,Python 历史上没有人会这样做或曾经这样做过。因此,可散列 == 不可变。
猜你喜欢
  • 1970-01-01
  • 2014-08-25
  • 2015-06-21
  • 1970-01-01
  • 2015-03-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-22
相关资源
最近更新 更多