具有更改__hash__的课程仍可使用字典访问

2024-05-26 11:10:31 发布

您现在位置:Python中文网/ 问答频道 /正文

我所做的显然不是人们想要做的事情,相反,我只是在测试为给定的类实现__hash__。在

我想看看如果在字典中添加一个虚假的“hashable”类,然后更改它的哈希值,是否会导致它无法访问它。在

我的班级是这样的:

class PhonyHash:

    def __hash__(self):
        val = list("A string")
        return id(val)  # always different

在我的IPython控制台中执行以下操作:

^{pr2}$

然后尝试使用d[p]访问元素会起作用:

>>> d[p]
"a value"

我明白,这不是应该做的事情,我真的很好奇为什么它会起作用。dict不使用对象的hash()来存储/检索它吗?为什么这个有用?在

编辑:如@VPfBsets的评论所述,由于某些原因,其行为符合预期:

>>> p = PhonyHash()
>>> s = {p}
>>> p in s
False

Tags: selfidstringreturn字典defvalhash
2条回答

这是一个奇怪的命运。一些CPython的机器阻碍了你。这三个问题是:

  1. 支持dict的数组的初始大小为8[1]
  2. CPython中的所有对象的内存地址都是模8[2]
  3. dict类有一个优化,检查键是否是同一个对象,如果为真则停止在那里(否则它将根据__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

CPython源的链接

  1. dictstruct定义了ma_table,它以ma_smalltable开头,长度为PyDict_MinSize。在
  2. 这在Objects/obmalloc.c中有记录
  3. 可以在查找函数herehere中看到

我有一个可能的解释:

根据这个来源:http://www.laurentluce.com/posts/python-dictionary-implementation/当保存dict元素的表很小时,只使用哈希的最后几位。在

id()号通常是一个机器地址,很可能与某个内存地址边界对齐。所以最后几位总是零,根本不是随机的。在结果中,您总是命中table[0]元素。在

尝试另一个随机伪哈希源会改变情况,并引发预期的keyror。在


编辑:沙丘用同样的方式回答了这个问题,他比我快。在

相关问题 更多 >