python dict实现细节

python dict implementation details

本问题已经有最佳答案,请猛点这里访问。

我有一个关于Python字典实现的问题。

似乎python将维护所有键的搜索顺序,例如如果您执行以下操作

1
2
3
4
5
a = {}
a[3] = 1
a[0] = 2

a = {0:2, 3:1}

python将自动更改我的插入顺序。正如python声称dict是无序集,我不太清楚理解为什么python会保持这样的搜索顺序。python是否通过哈希表实现dict并存储另一个是否设置为索引排序?

希望我能澄清这个问题。

谢谢你


dict的顺序完全由对象的散列函数(如果存在散列冲突,则插入顺序)决定。整数自身散列(至少到sys.maxint):

1
2
>>> hash(1)
1

(c)python实现获取对象的散列值,并使用一些位来确定表中的索引。它需要多少位取决于字典的长度。默认情况下,dict有8个可用插槽,因此数字08将发生冲突。我们可以看到如下:

1
2
3
4
5
6
7
8
9
10
11
>>> d1 = {}
>>> d1[0] = 'foo'
>>> d1[8] = 'bar'
>>> d1
{0: 'foo', 8: 'bar'}
>>>
>>> d2 = {}
>>> d2[8] = 'bar'
>>> d2[0] = 'foo'
>>> d2
{8: 'bar', 0: 'foo'}

由于08在我们的字典中发生冲突,插入顺序似乎保持不变。0取第一个可用插槽(毕竟,无论从0取多少位,都会得到08也试图占据这个位置。但是,如果占用了这个槽,冲突解决将接管,而python将在稍后的某个槽中插入该值。

当然,如果您的字典恰好有超过~5个元素,它将被调整大小(我认为是16个,但不要引用我的话),并且08将不再冲突…

1
2
3
4
5
6
7
8
9
10
>>> d1 = {x:x for x in range(1, 6)}
>>> d1[0] = 0
>>> d1[8] = 8
>>> d1
{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 8: 8}
>>> d2 = {x:x for x in range(1, 6)}
>>> d2[8] = 8
>>> d2[0] = 0
>>> d2
{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 8: 8}

注意,(排序的)顺序是保留的(不是插入顺序),这意味着每个整数在哈希表中都得到了它的首选位置(没有冲突)。我认为听写机在2/3rds满时会调整大小。

注意,这纯粹是学术性的——Python规范没有说明这是如何工作的,因此它可以随时更改。请不要依赖这种行为。其中大部分可以从源代码中的注释和它旁边的文档中收集…


dict索引排序只是如何实现dict的结果,不应该依赖于它。

准确地说,python不会更改插入顺序(因为它只是定义为将项目插入dict的顺序),但是迭代顺序没有保证。

当python创建dict时,它会为8个键、值对(我认为)创建足够的空间。对于空的听写,没有一个是满的。每当您将一个项放入dict中时,python都会获取该键的散列值,而该键的散列值决定了索引是什么。

如果您希望迭代顺序与插入顺序相同,请签出ordereddict。