如果一个对象的哈希值改变了会发生什么?

2024-06-26 14:11:44 发布

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

在Python中,我知道给定对象的__hash__返回的值在该对象的生存期内应该是相同的。但是,出于好奇,如果不是呢?这会造成什么样的破坏?在

class BadIdea(object):
  def __hash__(self):
    return random.randint(0, 10000)

我知道__contains__和{}的行为会很奇怪,因此dict和set的行为也会很奇怪。您还可能在dict/set中得到“孤立”值。在

还会发生什么?它会使解释器崩溃,或者破坏内部结构吗?在


Tags: 对象selfreturnobjectdefrandomhash解释器
2条回答

你的主要问题确实是关于dicts和set。如果您将一个对象插入dict/set中,并且该对象的哈希值发生了变化,那么当您试图检索该对象时,您将在dict/set的底层数组中的一个不同的点中查找,因此找不到该对象。这正是dict键应该总是不可变的原因。在

下面是一个小例子:假设我们将o放入dict中,o的初始哈希为3。我们可以这样做(稍微简化一下,但能让人理解):

Hash table:

  0   1   2   3   4   5   6   7
+ -+ -+ -+ -+ -+ -+ -+ -+
|   |   |   | o |   |   |   |   |
+ -+ -+ -+ -+ -+ -+ -+ -+
              ^
              we put o here, since it hashed to 3

现在让我们假设o的散列变为6。如果我们想从dict中检索o,我们将查看spot6,但是那里什么也没有!这将导致查询数据结构时出现假阴性。实际上,在dict的情况下,上面数组的每个元素都可以有一个与之相关联的“值”,并且在一个点上可以有多个元素(例如a hash collision)。此外,在决定将元素放在何处时,我们通常将哈希值取数组大小的模。尽管不考虑所有这些细节,上面的示例仍然准确地表达了当对象的哈希代码发生变化时可能出现的错误。在

Could it crash the interpreter, or corrupt internal structures?

不,这不会发生的。当我们说一个对象的散列变化是“危险的”时,我们的意思是危险的,因为它本质上违背了散列的目的,并使代码很难甚至无法推理。我们的意思并不是说危险会导致车祸。在

在Github上有一个很好的帖子:What happens when you mess with hashing。 首先,您需要知道Python期望(引用本文):

  • 对象的哈希在对象的整个生命周期中不会改变(换句话说,哈希对象应该是不可变的)。

  • a == b表示hash(a) == hash(b)(注意,在哈希冲突的情况下,反之可能不成立)。

下面是一个代码示例,它展示了变量散列的问题,有一个稍有不同的类示例,但思想保持不变:

>>> class Bad(object): 
...     def __init__(self, arg): 
...         self.arg = arg 
...     def __hash__(self): 
...         return hash(self.arg) 
... 
>>> Bad(1) 
<__main__.Bad object at ...> 
>>> hash(Bad(1)) 
1 
>>> a = Bad(1) 
>>> b = {a:1} 
>>> a.arg = 2 
>>> hash(a) 
2 
>>> b[a] 
Traceback (most recent call last):
...
KeyError: <__main__.Bad object at ...>

Here, we implicitly changed the hash of a by mutating the argument of a that is used to compute the hash. As a result, the object is no longer found in a dictionary, which uses the hash to find the object.

Note that Python doesn't prevent me from doing this. I could make it if I want, by making __setattr__ raise AttributeError, but even then I could forcibly change it by modifying the object's __dict__. This is what is meant when we say that Python is a "consenting adults" language.

它不会使Python崩溃,但是dict/set和基于对象哈希的所有内容都将发生意外行为。在

相关问题 更多 >