我所做的显然不是人们想要做的事情,而是我只是测试为给定的类实现__hash__
。
我想看看是否在词典中添加了一个虚假的'hashable'类,然后更改它的哈希值会导致它无法访问它。
我的班级看起来像这样:
class PhonyHash:
def __hash__(self):
val = list("A string")
return id(val) # always different
在我的IPython
控制台中执行以下操作:
>>> p = PhonyHash()
>>> d = { p: "a value"}
>>> hash(p) # changes hash
然后尝试使用d[p]
访问元素:
>>> d[p]
"a value"
我明白,这不是应该做的事情,我真的很好奇为什么它有效。 dict
不使用对象的hash()
来存储/检索它吗?为什么这有效?
编辑,正如@VPfB sets
的评论中所述,出于某种原因符合预期:
>>> p = PhonyHash()
>>> s = {p}
>>> p in s
False
答案 0 :(得分:5)
这是一个奇怪的命运。几台CPython机器挫败了你。现在的三个问题是:
dict
的数组的初始大小为8 [1] dict
类有一个优化,它检查键是同一个对象,如果为true则停在那里(否则它将根据__eq__
方法检查它们是否相等) [3] 这意味着尽管您的对象始终生成不同的哈希值,但将要检查的后备阵列的第一个槽是相同的。如果您没有收到密钥错误,因为广告位是空的。 dict
然后决定它有正确的键,因为它具有完全相同的对象,而不仅仅是一个相等的对象。
class PhonyHash:
_hash = 1
def __hash__(self):
return self._hash
p = PhonyHash()
d = {p: "val"}
print(p in d) # True
p._hash = 2
print(p in d) # False
p._hash = 9 # 9 % 8 == 1
print(p in d) # True
dict
struct定义ma_table
,其开头为ma_smalltable
,其长度为PyDict_MinSize。答案 1 :(得分:3)
我有一个可能的解释:
根据这个来源:http://www.laurentluce.com/posts/python-dictionary-implementation/当持有dict元素的表很小时,只使用散列的最后几位。
id()号通常是一个机器地址,很可能是一些内存地址边界。所以最后几位总是为零,而不是随机的。在结果中,您总是遇到table [0]元素。
尝试使用随机虚假哈希的不同来源会改变这种情况,并抛出预期的KeyError。
编辑:Dunes以同样的方式回答了问题,他比我快。