我正在学习python,提出的要点之一是变量名分别存储在内存中,即它们所引用的对象。也就是说,它们有一些指向内存中不同区域的指针,这些区域实际上是所存储的对象。
我现在正在阅读什么是哈希表以及如何实现它们的(基本)知识。这个问题的答案确实很有帮助:How does a hash table work?
我从中得出的结论是,如果我有一个键值对,那么哈希本质上会将索引转换为键,然后将该键存储在数组中。因此,如果“ key1”的索引为0,则a [0]包含实际值“ value1”。
但是,我不确定是否确实如此,还是不确定像python中的变量那样,数组中的值实际上不表示'value1',而是一些指向内存中'value1'位置的指针' 被储存了。还是去'key1'->数组索引-> a [array index]->值,还是去'key1'->数组索引-> a [array index]->指针地址- ->'value1'存储在由指针地址确定的存储位置中吗?
作为后续问题,如果是后者,那是否意味着存储在哈希表中的值实际上分散在整个内存中,而不是顺序存储?非常感谢,如果问题很明显,我们深表歉意。
答案 0 :(得分:1)
警告:这个答案可能有点令人困惑,因为要考虑两个单独的问题:
Python具有内置的哈希表类型dict
。它的内部实现是用C编写的(至少对于CPython而言),并使用了一些无法直接用Python编写的技巧。有关多年来使用的(几种)实现的详细信息,请参见How are Python's Built In Dictionaries Implemented。
Python作为一种语言,大多数没有数组。 1 链接问题的某些答案是用数组表示的,而Python的内置是 list 类型可以像数组一样使用,因此可以用作模型。那就是我在这里要做的。
因此,让我们开始制作一个空的伪数组:[]
。我们将其绑定到一些“类似数组”的名称:
a = []
然后回答您的问题。
1 array
模块提供C样式的数组。有关详细信息,请参见the array documentation。
我从中得出的结论是,如果我有一个键值对,那么哈希本质上会将索引转换为键,然后将该键存储在数组中。
反之亦然:哈希将密钥(“太大”)转换为计算机可以更轻松,直接地处理的更小(即哈希)值。它将密钥转换为索引。不过,您在问题的下一部分中对此是正确的:
因此,如果
'key1'
的索引为0,则a[0]
包含实际值'value1'
。
通常,是的。但是,如果哈希表既可以用于命中,也可以用于未命中,并且某些其他键(例如'1frotz'
)也可以 转换为索引0,则必须在{{ 1}},或保留实际键的并行数组或沿这些方向排列的东西,以确保a[0]
持有a[0]
而不持有其他键/值对。也就是说,在Python中,我们可以这样做:
'key1'
当然,我们还需要能够将项目放入哈希表。通常,当我们这样做时,如果键值对不适合,我们将扩展表格,因此,我们代替了上面的i = hash(key) % tablesize # assuming a fixed table size
assert i < len(a) # this is going to fail since len(a) == 0!
kv_pair = a[i]
assert kv_pair[0] == key
... use kv_pair[1], which holds the value ...
,我们这样做:
assert
当我们点击“复杂部分”时,或者数组本身太小,或者我们要使用的插槽已经用于某些 other 键。
在这里哈希表变得花哨。有些使用辅助哈希函数,执行称为 re-hashing 的操作,并探测其他表槽(在这种情况下,我们希望以非零的表大小开始)。有些将表扩展到位。同样,要了解Python的实际功能,请查看其他问题及其答案。
但是,我不确定是否确实如此,还是不确定像python中的变量那样,数组中的值实际上不表示'value1',而是一些指向内存中'value1'位置的指针' 被储存了。 ...
由于Python允许字典中使用动态类型,因此任何散列键的值槽中肯定会存储指向实际值的指针,而不是该值的副本。不同类型的值具有不同的基础大小。您可以使用def find_value(key):
if len(a) > 0:
i = hash(key) % len(a)
kv_pair = a[i]
if kv_pair is not None and kv_pair[0] == key:
return kv_pair[1]
return None
def insert_kv_pair(key, value):
if len(a) > 0:
i = hash(key) % len(a)
kv_pair = a[i]
if kv_pair is None: # not in the table yet
a[i] = [key, value] # so put it in
return
if kv_pair[0] == key: # already in the table
kv_pair[1] = value # so replace the value
return
... the complicated parts go here ...
查看类型的大小(但也请参见How do I determine the size of an object in Python?):
sys.getsizeof
由于大小在整个地图上都不同,因此Python只会将指针存储在给定键的字典的“值”插槽中。
...这是否意味着散列表中存储的值实际上分散在整个内存中,而不是顺序存储?
是的。在内部Python实现中,仅哈希值和一对键/值指针按顺序存储。
答案 1 :(得分:1)
如果您查看所看到的Python字典的基本查找功能的source code,它们将为实际值分配一个指针。
在该方法中,您还可以使用给定的键查看查找的所有步骤。所以我认为你的假设
'key1' --> array index --> a[array index] --> pointer address --> 'value1'
是正确的。