【问题标题】:Why is a tuple containing an unhashable type unhashable?为什么包含不可散列类型的元组不可散列?
【发布时间】:2017-10-17 04:53:58
【问题描述】:

例如,元组(1,[0,1,2])。我从设计的角度理解为什么;如果元组仍然是可散列的,那么通过将任何不可散列的类型包装在元组中来使其可散列是微不足道的,这会破坏散列性的正确行为,因为您可以更改对象的值而不更改元组的散列值.但是,如果元组不可散列,那么我不明白是什么让对象可散列——我认为它只需要实现__hash__(self),元组就是这样做的。

根据我看过的其他答案以及测试示例,似乎这样的对象是不可散列的。 tuple.__hash__() 为其组件对象调用 __hash__ 似乎是明智的行为,但我不明白从实现的角度来看这是如何工作的,例如我不知道当字典仍然是类型元组并且元组仍然定义 __hash__ 时,它是如何将其识别为不可散列类型的。

【问题讨论】:

  • “为什么包含不可散列类型的元组不可散列?”答案就在问题中;)
  • 定义了__hash__ 函数并不意味着所有可能的元组都是可散列的。就像定义了逆函数并不意味着 0 有逆函数。
  • 此外,您可以轻松定义一个自定义类,该类实现了__hash__,但它是可变的。当然,如果您将该类的实例用作 dict 键或设置项,然后对实例进行变异,则可能会发生奇怪的事情。 ;)

标签: python


【解决方案1】:

tuple 通过计算和组合它包含的值的哈希值来实现自己的哈希值。当散列其中一个值失败时,它会让生成的异常不受阻碍地传播。

不可散列只是意味着对你调用hash()会触发TypeError;一种方法是不定义__hash__ 方法,但如果在__hash__ 方法的过程中通过其他方式引发TypeError(或任何其他错误),它同样有效。

基本上,tuple 是一个可散列的类型(isinstance((), collections.abc.Hashable) 为真,isinstance(([],), collections.abc.Hashable) 也是如此,因为它是一个类型级别检查 __hash__ 的存在),但如果它存储不可散列的类型,任何尝试计算哈希将在使用时引发异常,因此在该场景中它表现类似于不可哈希的类型。

【讨论】:

  • 我没有认为“不可散列”意味着“在散列时产生错误”而不是“永远不能散列”。现在很清楚了,谢谢!事实上,这解释了为什么 d = {(1,[]) : 0} 引发 TypeError: unhashable type <list> ——该异常是由 tuple.__hash__() 引发的,而不是 dict.__hash__()
【解决方案2】:

我假设tuple.__hash__() 为元组中的每个项目调用hash(item),然后对结果进行异或运算。如果其中一项不可散列,则将引发TypeError 冒泡到原始调用者。

猜你喜欢
  • 2016-09-05
  • 1970-01-01
  • 2015-07-05
  • 2017-07-11
  • 2016-03-16
  • 2016-12-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多