键的地址相互之间存储得很远

时间:2018-10-21 13:10:42

标签: python

我想浏览哈希表,

In [1]: book = {"apple":0.67, "milk":1.49, "avocado":1.49, "python":2}   
In [5]: [hex(id(key)) for key in book]                                                                            
Out[5]: ['0x10ffffc70', '0x10ffffab0', '0x10ffffe68', '0x10ee1cca8']

地址表明键之间的距离很远,尤其是键“ python”,
我以为它们彼此相邻。

这怎么可能发生?它以高性能运行吗?

1 个答案:

答案 0 :(得分:8)

我们可以通过两种方式来解释您的困惑:您希望id()是键的哈希函数,或者您希望键被重定位到哈希表,并且在CPython中,{{ 1}}值是一个存储位置,id()值将说明哈希表的大小。我们可以通过谈论Python的字典实现以及Python一般如何处理对象来解决这两个问题。

Python字典以hash table的形式实现,这是一个有限大小的表。为了存储密钥,哈希函数生成一个整数(相等的整数表示相等的值),然后使用模函数将密钥基于该数字存储在插槽中。

id()

这可能导致冲突,因此为哈希函数选择大量的数字将有助于减少发生此类冲突的机会。无论如何,您仍然必须处理碰撞,但是您希望将其最小化。

Python在这里不使用slot = hash(key) % len(table) 函数作为哈希函数,因为对于相等的值,它不会产生相同的哈希!如果您没有为相同的值产生相同的哈希,那么您将无法使用多个id()字符串作为再次找到正确插槽的方法,因为"hello world"然后dictionary["hello world"] = "value"会产生不同的"hello world" in dictionary值,因此散列到不同的插槽,并且您不会将特定的字符串值用作键。

相反,期望对象实现__hash__ method,并且您可以使用hash() function看到该方法对各种对象产生的作用。

由于存储在字典中的键必须保持不变,Python不允许您将可变类型存储在字典中。否则,如果您可以更改其值,则它们将不再等于具有旧值和可耻哈希值的另一个此类对象,并且您将不会在其新哈希值映射到的插槽中找到它们。

请注意,Python将所有对象放在dynamic heap中,并在各处使用引用来关联对象。字典包含对键和值的引用;将密钥放入字典中不会将密钥重新定位在内存中,并且密钥的id()不会改变。如果键被重定位,则将违反id()函数的要求,documentation指出:这是一个整数,可以保证此对象在其生存期内唯一且恒定< / em>。

对于那些冲突:Python通过查找具有固定公式的新插槽,在可预测但伪随机的插槽编号系列中找到一个空插槽来处理碰撞;如果您想了解详细信息,请参见dictobject.c source code comments。当表格填满时,Python会动态增加表格以容纳更多元素,因此始终会有空插槽。